From: root Date: Wed, 16 Mar 2005 20:49:59 +0000 (+0000) Subject: Initial revision X-Git-Url: https://git.rkrishnan.org/pf/content/en/seg/bcase/index.php?a=commitdiff_plain;h=0156693d5da49b097b0083c264d616cdb97b5c74;p=dttsp.git Initial revision --- 0156693d5da49b097b0083c264d616cdb97b5c74 diff --git a/jDttSP/DttSPrc b/jDttSP/DttSPrc new file mode 100644 index 0000000..84a0397 --- /dev/null +++ b/jDttSP/DttSPrc @@ -0,0 +1,2 @@ +setMode CWL +setFilter -1000 -500 diff --git a/jDttSP/Makefile b/jDttSP/Makefile new file mode 100644 index 0000000..4c39051 --- /dev/null +++ b/jDttSP/Makefile @@ -0,0 +1,62 @@ +CFLAGS = -g -O3 -I. -I/usr/local/include +#CFLAGS = -g -I. -I/usr/local/include +LIBS = -L/usr/local/lib -ljack -lpthread -lfftw -lm +#LIBS = -lefence -L/usr/local/lib -ljack -lpthread -lfftw -lm + +staticlibname=libDttSP.a + +OBJ = am_demod.o\ + banal.o\ + bufvec.o\ + chan.o\ + chap.o\ + correctIQ.o\ + crc16.o\ + cxops.o\ + digitalagc.o\ + fastrig.o\ + filter.o\ + fm_demod.o\ + lmadf.o\ + noiseblanker.o\ + oscillator.o\ + ovsv.o\ + ringb.o\ + sdr.o\ + sdrexport.o\ + speechproc.o\ + splitfields.o\ + spottone.o\ + thunk.o\ + window.o\ + update.o + +jsdr: main.o $(OBJ) + $(CC) -o jsdr main.o $(OBJ) $(LIBS) + +$(OBJ): sdrexport.h + +metermon: metermon.o chan.o ringb.o bufvec.o cxops.o banal.o + $(CC) -o metermon metermon.o chan.o ringb.o bufvec.o cxops.o banal.o $(LIBS) + +mkchan: mkchan.o bufvec.o banal.o cxops.o + $(CC) -o mkchan mkchan.o bufvec.o banal.o cxops.o $(LIBS) + +ipc: + ./setup-ipc + +obj: $(OBJ) + +clean: + /bin/rm *.o jsdr mkchan metermon $(staticlibname) + +staticlib: $(OBJ) + ar rcs $(staticlibname) $(OBJ) + ranlib $(staticlibname) + +#CFLAGS = -fPIC -g -O3 -I. -I/usr/local/include +# sharedlibname=libDttSP.so +# sharedlibvers=0.0.1 +# sharedlib=$(sharedlibname).$(sharedlibvers) +# sharedlib: $(OBJ) +# gcc -shared -Wl,-soname,$(sharedlib) -o $(sharedlib) $(OBJ) -lc diff --git a/jDttSP/am_demod.c b/jDttSP/am_demod.c new file mode 100644 index 0000000..61750ca --- /dev/null +++ b/jDttSP/am_demod.c @@ -0,0 +1,134 @@ +/* am_demod.c */ + +#include +#include + +/*------------------------------------------------------------------------------*/ +/* private to AM */ +/*------------------------------------------------------------------------------*/ + +static void +init_pll(AMD am, + REAL samprate, + REAL freq, + REAL lofreq, + REAL hifreq, + REAL bandwidth) { + REAL fac = TWOPI / samprate; + am->pll.freq.f = freq * fac; + am->pll.freq.l = lofreq * fac; + am->pll.freq.h = hifreq * fac; + am->pll.phs = 0.0; + am->pll.delay = cxJ; + + am->pll.iir.alpha = bandwidth * fac; /* arm filter */ + am->pll.alpha = am->pll.iir.alpha * 0.3; /* pll bandwidth */ + am->pll.beta = am->pll.alpha * am->pll.alpha * 0.25; /* second order term */ + am->pll.fast_alpha = am->pll.alpha; +} + +static void +pll(AMD am, COMPLEX sig) { + COMPLEX z = Cmplx(cos(am->pll.phs), sin(am->pll.phs)); + REAL diff; + + am->pll.delay.re = z.re * sig.re + z.im * sig.im; + am->pll.delay.im = -z.im * sig.re + z.re * sig.im; + diff = Cmag(sig) * ATAN2(am->pll.delay.im, am->pll.delay.re); + + am->pll.freq.f += am->pll.beta * diff; + + if (am->pll.freq.f < am->pll.freq.l) + am->pll.freq.f = am->pll.freq.l; + if (am->pll.freq.f > am->pll.freq.h) + am->pll.freq.f = am->pll.freq.h; + + am->pll.phs += am->pll.freq.f + am->pll.alpha * diff; + + while (am->pll.phs >= TWOPI) am->pll.phs -= TWOPI; + while (am->pll.phs < 0) am->pll.phs += TWOPI; +} + +static double +dem(AMD am) { + am->lock.curr = 0.999 * am->lock.curr + 0.001 * fabs(am->pll.delay.im); + + /* env collapse? */ + /* if ((am->lock.curr < 0.05) && (am->lock.prev >= 0.05)) { + am->pll.alpha = 0.1 * am->pll.fast_alpha; + am->pll.beta = am->pll.alpha * am->pll.alpha * 0.25; + } else if ((am->pll.alpha > 0.05) && (am->lock.prev <= 0.05)) { + am->pll.alpha = am->pll.fast_alpha; + }*/ + am->lock.prev = am->lock.curr; + am->dc = 0.99*am->dc + 0.01*am->pll.delay.re; + return am->pll.delay.re-am->dc; +} + +/*------------------------------------------------------------------------------*/ +/* public */ +/*------------------------------------------------------------------------------*/ + +void +AMDemod(AMD am) { + int i; + double demout; + switch (am->mode) { + case SAMdet: + for (i = 0; i < am->size; i++) { + pll(am, CXBdata(am->ibuf, i)); + demout = dem(am); + CXBdata(am->obuf, i) = Cmplx(demout, demout); + } + break; + case AMdet: + for (i = 0; i < am->size; i++) { + am->lock.curr = Cmag(CXBdata(am->ibuf, i)); + am->dc = 0.9999 * am->dc + 0.0001 * am->lock.curr; + am->smooth = 0.5 * am->smooth + 0.5 * (am->lock.curr - am->dc); + /* demout = am->smooth; */ + CXBdata(am->obuf, i) = Cmplx(am->smooth, am->smooth); + } + break; + } +} + +AMD +newAMD(REAL samprate, + REAL f_initial, + REAL f_lobound, + REAL f_hibound, + REAL f_bandwid, + int size, + COMPLEX *ivec, + COMPLEX *ovec, + AMMode mode, + char *tag) { + AMD am = (AMD) safealloc(1, sizeof(AMDDesc), tag); + + am->size = size; + am->ibuf = newCXB(size, ivec, tag); + am->obuf = newCXB(size, ovec, tag); + am->mode = mode; + init_pll(am, + samprate, + f_initial, + f_lobound, + f_hibound, + f_bandwid); + + am->lock.curr = 0.5; + am->lock.prev = 1.0; + am->dc = 0.0; + + return am; +} + +void +delAMD(AMD am) { + if (am) { + delCXB(am->ibuf); + delCXB(am->obuf); + safefree((char *) am); + } +} diff --git a/jDttSP/am_demod.h b/jDttSP/am_demod.h new file mode 100644 index 0000000..8b63460 --- /dev/null +++ b/jDttSP/am_demod.h @@ -0,0 +1,58 @@ +/* am_demod.h */ + +#ifndef _am_demod_h +#define _am_demod_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef enum _ammode {AMdet, SAMdet} AMMode; +typedef +struct _am_demod { + int size; + CXB ibuf, obuf; + + struct { + REAL alpha, beta,fast_alpha; + struct { REAL f, l, h; } freq; + REAL phs; + struct { REAL alpha; } iir; + COMPLEX delay; + } pll; + + struct { REAL curr, prev; } lock; + + REAL dc; + REAL smooth; + AMMode mode; +} AMDDesc, *AMD; + +extern void AMDemod(AMD am); +extern AMD newAMD(REAL samprate, + REAL f_initial, + REAL f_lobound, + REAL f_hibound, + REAL f_bandwid, + int size, + COMPLEX *ivec, + COMPLEX *ovec, + AMMode mode, + char *tag); +extern void delAMD(AMD am); + +#ifndef TWOPI +#define TWOPI (2.0*M_PI) +#endif + +#endif diff --git a/jDttSP/banal.c b/jDttSP/banal.c new file mode 100644 index 0000000..a5e8447 --- /dev/null +++ b/jDttSP/banal.c @@ -0,0 +1,197 @@ +/* banal.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include +#include + +void +nilfunc(void) {} + +double +sqr(double x) { return x * x; } + +int +popcnt(int k) { + int c, i; + c = k & 01; + for (i = 1; i < 32; i++) c += (k >> i) & 01; + return c; +} + +int +npoof2(int n) { + int i = 0; + --n; + while (n > 0) n >>= 1, i++; + return i; +} + +int +nblock2(int n) { return 1 << npoof2(n); } + +int +in_blocks(int count, int block_size) { + if (block_size < 1) { + fprintf(stderr, "block_size zero in in_blocks\n"); + exit(1); + } + return (1 + ((count - 1) / block_size)); +} + +FILE * +efopen(char *path, char *mode) { + FILE *iop = fopen(path, mode); + if (!iop) { + fprintf(stderr, "can't open \"%s\" in mode \"%s\"\n", path, mode); + exit(1); + } + return iop; +} + +FILE * +efreopen(char *path, char *mode, FILE *strm) { + FILE *iop = freopen(path, mode, strm); + if (!iop) { + fprintf(stderr, "can't reopen \"%s\" in mode \"%s\"\n", path, mode); + exit(1); + } + return iop; +} + +size_t +filesize(char *path) { + struct stat sbuf; + if (stat(path, &sbuf) == -1) return -1; + return sbuf.st_size; +} + +size_t +fdsize(int fd) { + struct stat sbuf; + if (fstat(fd, &sbuf) == -1) return -1; + return sbuf.st_size; +} + +#define MILLION (1000000) + +// return current tv +struct timeval +now_tv(void) { + struct timeval tv; + gettimeofday(&tv, 0); + return tv; +} + +// return ta - tb +struct timeval +diff_tv(struct timeval *ta, struct timeval *tb) { + struct timeval tv; + if (tb->tv_usec > ta->tv_usec) { + ta->tv_sec--; + ta->tv_usec += MILLION; + } + tv.tv_sec = ta->tv_sec - tb->tv_sec; + if ((tv.tv_usec = ta->tv_usec - tb->tv_usec) >= MILLION) { + tv.tv_usec -= MILLION; + tv.tv_sec++; + } + return tv; +} + +// return ta + tb +struct timeval +sum_tv(struct timeval *ta, struct timeval *tb) { + struct timeval tv; + tv.tv_sec = ta->tv_sec + tb->tv_sec; + if ((tv.tv_usec = ta->tv_usec + tb->tv_usec) >= MILLION) { + tv.tv_usec -= MILLION; + tv.tv_sec++; + } + return tv; +} + +char * +fmt_tv(struct timeval *tv) { + static char buff[256]; + snprintf(buff, sizeof(buff), "%ds%du", tv->tv_sec, tv->tv_usec); + return buff; +} + +char * +since(struct timeval *tv) { + struct timeval nt = now_tv(), + dt = diff_tv(&nt, tv); + return fmt_tv(&dt); +} + +// linear integer interpolation: +// real vector v, n long, -> real vector u, m long +// *** n must divide m +// returns actual number of valid points in u +// (== n - m/n since v[n] is undefined) + +int +hinterp_vec(REAL *u, int m, REAL *v, int n) { + if (!u || !v || (n < 2) || (m < n) || (m % n)) return 0; + else { + int div = m / n, i, j = 0; + for (i = 1; i < n; i++) { + int k; + REAL vl = v[i - 1], del = (v[i] - vl) / div; + u[j++] = vl; + for (k = 1; k < div; k++) u[j++] = vl + k * del; + } + u[j++] = v[n - 1]; + return j; + } +} + +void +status_message(char *msg) { write(2, msg, strlen(msg)); } + +FILE * +find_rcfile(char *base) { + char path[MAXPATHLEN]; + FILE *fp; + sprintf(path, "./%s", base); + if ((fp = fopen(path, "r"))) return fp; + else { + char *home = getenv("HOME"); + if (!home) + fprintf(stderr, "can't get HOME!\n"), exit(1); + sprintf(path, "%s/%s", home, base); + if ((fp = fopen(path, "r"))) return fp; + } + return 0; +} + diff --git a/jDttSP/banal.h b/jDttSP/banal.h new file mode 100644 index 0000000..1ca3ffe --- /dev/null +++ b/jDttSP/banal.h @@ -0,0 +1,82 @@ +/* banal.h + stuff we're too embarrassed to declare otherwise + + This file is part of a program that implements a Software-Defined Radio. + + Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + The authors can be reached by email at + + ab2kt@arrl.net + or + rwmcgwier@comcast.net + + or by paper mail at + + The DTTS Microwave Society + 6 Kathleen Place + Bridgewater, NJ 08807 +*/ + +#ifndef _banal_h + +#define _banal_h + +#include +#include + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#define abs(a) ((a) >= 0 ? (a) : -(a)) + +#define MONDO 1e15 +#define BITSY 1e-15 + +#define TRUE 1 +#define FALSE 0 + +extern void nilfunc(void); +extern double sqr(double); +extern int popcnt(int); +extern int npoof2(int); +extern int nblock2(int); + +extern int in_blocks(int count, int block_size); + +extern FILE *efopen(char *path, char *mode); +extern FILE *efreopen(char *path, char *mode, FILE *strm); +extern size_t filesize(char *path); +extern size_t fdsize(int fd); + +extern struct timeval now_tv(void); +extern struct timeval diff_tv(struct timeval *, struct timeval *); +extern struct timeval sum_tv(struct timeval *, struct timeval *); +extern char *fmt_tv(struct timeval *); +extern char *since(struct timeval *); +extern struct timeval now_tv(void); + +extern int hinterp_vec(REAL *, int, REAL *, int); + +extern void status_message(char *msg); + +extern FILE *find_rcfile(char *base); + +#endif diff --git a/jDttSP/bufvec.c b/jDttSP/bufvec.c new file mode 100644 index 0000000..606992b --- /dev/null +++ b/jDttSP/bufvec.c @@ -0,0 +1,203 @@ +/* bufvec.c + creation, deletion, management for vectors and buffers + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +/*------------------------------------------------------------------------*/ +/* wrapper around calloc */ + +char * +safealloc(int count, int nbytes, char *tag) { + char *p = calloc(count, nbytes); + if (!p) { + if (tag && *tag) fprintf(stderr, "safealloc: %s\n", tag); + else perror("safealloc"); + exit(1); + } + return p; +} + +void +safefree(char *p) { if (p) free(p); } + +/*------------------------------------------------------------------------*/ +/* allocate/free just vectors */ + +REAL * +newvec_REAL(int size, char *tag) { + return (REAL *) safealloc(size, sizeof(REAL), tag); +} + +void +delvec_REAL(REAL *vec) { if (vec) free((char *) vec); } + +IMAG * +newvec_IMAG(int size, char *tag) { + return (IMAG *) safealloc(size, sizeof(IMAG), tag); +} + +void +delvec_IMAG(IMAG *vec) { if (vec) free((char *) vec); } + +COMPLEX * +newvec_COMPLEX(int size, char *tag) { + return (COMPLEX *) safealloc(size, sizeof(COMPLEX), tag); +} + +void +delvec_COMPLEX(COMPLEX *vec) { if (vec) free((char *) vec); } + +/*------------------------------------------------------------------------*/ +/* buffers (mainly i/o) */ +/*------------------------------------------------------------------------*/ +/* complex */ + +CXB +newCXB(int size, COMPLEX *base, char *tag) { + CXB p = (CXB) safealloc(1, sizeof(CXBuffer), tag); + if (base) { + CXBbase(p) = base; + CXBmine(p) = FALSE; + } else { + CXBbase(p) = newvec_COMPLEX(size, "newCXB"); + CXBmine(p) = TRUE; + } + CXBsize(p) = CXBwant(p) = size; + CXBovlp(p) = CXBhave(p) = CXBdone(p) = 0; + return p; +} + +void +delCXB(CXB p) { + if (p) { + if (CXBmine(p)) delvec_COMPLEX(CXBbase(p)); + free((char *) p); + } +} + +/*------------------------------------------------------------------------*/ +/* real */ + +RLB +newRLB(int size, REAL *base, char *tag) { + RLB p = (RLB) safealloc(1, sizeof(RLBuffer), tag); + if (base) { + RLBbase(p) = base; + RLBmine(p) = FALSE; + } else { + RLBbase(p) = newvec_REAL(size, "newRLB"); + RLBmine(p) = TRUE; + } + RLBsize(p) = RLBwant(p) = size; + RLBovlp(p) = RLBhave(p) = RLBdone(p) = 0; + return p; +} + +void +delRLB(RLB p) { + if (p) { + delvec_REAL(RLBbase(p)); + free((char *) p); + } +} + +//======================================================================== +// return normalization constant + +REAL +normalize_vec_REAL(REAL *v, int n) { + if (v && (n > 0)) { + int i; + REAL big = -MONDO; + for (i = 0; i < n; i++) { + REAL a = abs(v[i]); + big = max(big, a); + } + if (big > 0.0) { + REAL scl = 1.0 / big; + for (i = 0; i < n; i++) v[i] *= scl; + return scl; + } else return 0.0; + } else return 0.0; +} + +REAL +normalize_vec_COMPLEX(COMPLEX *z, int n) { + if (z && (n > 0)) { + int i; + REAL big = -MONDO; + for (i = 0; i < n; i++) { + REAL a = Cabs(z[i]); + big = max(big, a); + } + if (big > 0.0) { + REAL scl = 1.0 / big; + for (i = 0; i < n; i++) z[i] = Cscl(z[i], scl); + return scl; + } else return 0.0; + } else return 0.0; +} + +/*------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ +/* mostly for debugging when necessary */ + +void +dump_REAL(FILE *fp, char *head, REAL *ptr, int beg, int fin) { + int i; + FILE *iop = fp? fp : stderr; + if (head && *head) fprintf(iop, "dump_REAL: %s\n", head); + for (i = beg; i < fin; i++) + fprintf(iop, "%5d %g\n", i, ptr[i]); +} + +void +dump_IMAG(FILE *fp, char *head, IMAG *ptr, int beg, int fin) { + int i; + FILE *iop = fp? fp : stderr; + if (head && *head) fprintf(iop, "dump_REAL: %s\n", head); + for (i = beg; i < fin; i++) + fprintf(iop, "%5d %g\n", i, ptr[i]); +} + +void +dump_CX(FILE *fp, char *head, COMPLEX *ptr, int beg, int fin) { + int i; + FILE *iop = fp? fp : stderr; + if (head && *head) fprintf(iop, "dump_CX: %s\n", head); + for (i = beg; i < fin; i++) + fprintf(iop, "%5d %g %g\n", i, ptr[i].re, ptr[i].im); +} + + diff --git a/jDttSP/bufvec.h b/jDttSP/bufvec.h new file mode 100644 index 0000000..3da0db6 --- /dev/null +++ b/jDttSP/bufvec.h @@ -0,0 +1,99 @@ +/* bufvec.h + +defs for vector and buffer data structures and utilities + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _bufvec_h + +#define _bufvec_h + +#include +#include +#include +#include + +typedef struct _complex_buffer_desc { + COMPLEX *data; + int size, ovlp, want, have, done, mine; +} CXBuffer, *CXB; + +/* all these should be OK rhs or lhs */ + +#define CXBbase(p) ((p)->data) +#define CXBdata(p, i) (CXBbase(p)[(i)]) +#define CXBreal(p, i) (CXBbase(p)[(i)].re) +#define CXBimag(p, i) (CXBbase(p)[(i)].im) +#define CXBsize(p) ((p)->size) +#define CXBovlp(p) ((p)->ovlp) +#define CXBwant(p) ((p)->want) +#define CXBhave(p) ((p)->have) +#define CXBdone(p) ((p)->done) +#define CXBmine(p) ((p)->mine) + +typedef struct _real_buffer_desc { + REAL *data; + int size, ovlp, want, have, done, mine; +} RLBuffer, *RLB; + +#define RLBbase(p) ((p)->data) +#define RLBdata(p, i) (RLBbase(p)[(i)]) +#define RLBsize(p) ((p)->size) +#define RLBovlp(p) ((p)->ovlp) +#define RLBwant(p) ((p)->want) +#define RLBhave(p) ((p)->have) +#define RLBdone(p) ((p)->done) +#define RLBmine(p) ((p)->mine) + +extern char *safealloc(int count, int nbytes, char *tag); +extern void safefree(char *p); +extern REAL *newvec_REAL(int size, char *tag); +extern void delvec_REAL(REAL *vec); +extern IMAG *newvec_IMAG(int size, char *tag); +extern void delvec_IMAG(IMAG *vec); +extern COMPLEX *newvec_COMPLEX(int size, char *tag); +extern void delvec_COMPLEX(COMPLEX *buf); +extern void dump_REAL(FILE *fp, char *head, REAL *ptr, int beg, int fin); +extern void dump_IMAG(FILE *fp, char *head, IMAG *ptr, int beg, int fin); +extern void dump_CX(FILE *fp, char *head, COMPLEX *ptr, int beg, int fin); + +extern CXB newCXB(int size, COMPLEX *base, char *tag); +extern void delCXB(CXB p); + +extern RLB newRLB(int size, REAL *base, char *tag); +extern void delRLB(RLB p); + +extern REAL normalize_vec_REAL(REAL *, int); +extern REAL normalize_vec_COMPLEX(COMPLEX *, int); + +#endif + diff --git a/jDttSP/chan.c b/jDttSP/chan.c new file mode 100644 index 0000000..651274c --- /dev/null +++ b/jDttSP/chan.c @@ -0,0 +1,106 @@ +/* chan.c + +fast inter-user-process single-reader/single-writer channels +using a modified version of jack ringbuffers and memory-mapped files. + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +size_t +putChan(Chan c, char *data, size_t size) { + return ringb_write(c->rb, data, size); +} + +size_t +getChan(Chan c, char *data, size_t size) { + return ringb_read(c->rb, data, size); +} + +BOOLEAN +putChan_nowait(Chan c, char *data, size_t size) { + if (ringb_write_space(c->rb) >= size) { + ringb_write(c->rb, data, size); + return TRUE; + } else return FALSE; +} + +BOOLEAN +getChan_nowait(Chan c, char *data, size_t size) { + if (ringb_read_space(c->rb) >= size) { + ringb_read(c->rb, data, size); + return TRUE; + } else return FALSE; +} + +Chan +openChan(char *path, size_t want) { + Chan c = (Chan) safealloc(sizeof(ChanDesc), 1, "Chan header"); + c->size.rib = sizeof(ringb_t); + c->size.buf = nblock2(want); + c->file.path = path; + if ((c->file.desc = open(c->file.path, O_RDWR)) == -1) { + fprintf(stderr, "can't open Chan file %s\n", c->file.path); + exit(1); + } + c->size.tot = c->size.rib + c->size.buf; + if ((c->size.tru = fdsize(c->file.desc)) < c->size.tot) { + fprintf(stderr, + "Chan file %s is too small (%d) for required size (%s)\n", + c->file.path, c->size.tru, c->size.tot); + exit(1); + } + if (!(c->map.base = (char *) mmap(0, + c->size.tot, + PROT_READ | PROT_WRITE, + MAP_SHARED, + c->file.desc, + 0))) { + fprintf(stderr, "can't memory map %s for Chan\n", + c->file.path); + exit(1); + } + if (!(c->rb = ringb_create(c->map.base, c->size.buf))) { + fprintf(stderr, "can't map RB for Chan\n"); + exit(1); + } + return c; +} + +void +closeChan(Chan c) { + if (c) { + close(c->file.desc); + munmap(c->map.base, c->size.tot); + safefree((char *) c); + } +} diff --git a/jDttSP/chan.h b/jDttSP/chan.h new file mode 100644 index 0000000..bf92625 --- /dev/null +++ b/jDttSP/chan.h @@ -0,0 +1,83 @@ +/* chan.h + +fast inter-user-process single-reader/single-writer channels +using a modified version of jack ringbuffers and memory-mapped files. + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _chan_h +#define _chan_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef +struct _chan { + struct { + size_t buf, rib, tot, tru; + } size; + struct { + char *path; + int desc; + } file; + struct { + char *base; + } map; + ringb_t *rb; +} ChanDesc, *Chan; + +extern size_t putChan(Chan c, char *data, size_t size); +extern size_t getChan(Chan c, char *data, size_t size); +extern BOOLEAN putChan_nowait(Chan c, char *data, size_t size); +extern BOOLEAN getChan_nowait(Chan c, char *data, size_t size); +// NB want will be rounded up to a power of 2 +extern Chan openChan(char *path, size_t want); +extern void closeChan(Chan c); + +#endif diff --git a/jDttSP/chap.c b/jDttSP/chap.c new file mode 100644 index 0000000..474e81c --- /dev/null +++ b/jDttSP/chap.c @@ -0,0 +1,109 @@ +/* chap.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +REAL +ChAp_eval(ChAp ca, REAL x, BOOLEAN *err) { + int i; + REAL d, e, u, v; + + if (x < ChApLob(ca)) { + x = ChApLob(ca); + if (err) *err = TRUE; + } else if (x > ChApHib(ca)) { + x = ChApHib(ca); + if (err) *err = TRUE; + } else if (err) *err = FALSE; + + d = e = 0.0; + u = (2.0 * x - ChApDif(ca)) / ChApDif(ca); + v = 2.0 * u; + for (i = ChApLen(ca) - 1; i > 0; --i) { + REAL sv = d; + d = v * d - e + ChApCoef(ca, i); + e = sv; + } + + return u * d - e + 0.5 * ChApCoef(ca, 0); +} + +ChAp +ChAp_fit(ChAp ca) { + int i, j, n = ChApLen(ca); + REAL (*func)(REAL) = ChApFunc(ca), + bma = 0.5 * (ChApHib(ca) - ChApLob(ca)), + bpa = 0.5 * (ChApHib(ca) + ChApLob(ca)), + fac = 2.0 / n, + *tbl = newvec_REAL(n, ChApTag(ca)); + for (j = 0; j < n; j++) { + REAL y = cos(M_PI * (j + 0.5) / n); + tbl[j] = (*func)(y * bma + bpa); + } + for (i = 0; i < n; i++) { + REAL sum = 0.0; + for (j = 0; j < n; j++) + sum += tbl[j] * cos((M_PI * i) * ((j + 0.5) / n)); + ChApCoef(ca, i) = fac * sum; + } + delvec_REAL(tbl); + return ca; +} + +ChAp +newChAp(REAL (*func)(REAL arg), + REAL lo, + REAL hi, + int len, + char *tag) { + ChAp ca = (ChAp) safealloc(1, sizeof(ChApDesc), tag); + ChApFunc(ca) = func; + ChApLen(ca) = len; + ChApLob(ca) = lo; + ChApHib(ca) = hi; + ChApDif(ca) = hi - lo; + ChApCoefBase(ca) = newvec_REAL(len, tag); + ChApTag(ca) = strdup(tag); + return ChAp_fit(ca); +} + +void +delChAp(ChAp ca) { + if (ca) { + safefree((char *) ChApCoefBase(ca)); + safefree(ChApTag(ca)); + safefree((char *) ca); + } +} + + diff --git a/jDttSP/chap.h b/jDttSP/chap.h new file mode 100644 index 0000000..a201ad9 --- /dev/null +++ b/jDttSP/chap.h @@ -0,0 +1,79 @@ +/* chap.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _chap_h +#define _chap_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +_cheby_approx { + REAL (*func)(REAL); + int len; + REAL *coef; + struct { REAL lo, hi, df; } lim; + char *tag; +} ChApDesc, *ChAp; + +#define ChApFunc(p) ((p)->func) +#define ChApLen(p) ((p)->len) +#define ChApLob(p) ((p)->lim.lo) +#define ChApHib(p) ((p)->lim.hi) +#define ChApDif(p) ((p)->lim.df) +#define ChApCoefBase(p) ((p)->coef) +#define ChApCoef(p, i) (((p)->coef)[i]) +#define ChApTag(p) ((p)->tag) + +extern REAL ChAp_eval(ChAp ca, REAL x, BOOLEAN *err); +extern ChAp ChAp_fit(ChAp ca); +extern ChAp newChAp(REAL (*func)(REAL arg), + REAL lo, + REAL hi, + int len, + char *tag); + +extern void delChAp(ChAp ca); + +#endif diff --git a/jDttSP/cmdr b/jDttSP/cmdr new file mode 100755 index 0000000..e979db5 --- /dev/null +++ b/jDttSP/cmdr @@ -0,0 +1,14 @@ +#!/bin/sh + +CMDPIPE=./IPC/SDR-1000-0-commands.fifo + +while true +do + read -p "command: " -e cmdline + if [ $? -ne 0 ] + then + break + fi + sendline="$cmdline" + echo $sendline +done | m4 -e enums.m4 - >$CMDPIPE diff --git a/jDttSP/command-vocabulary b/jDttSP/command-vocabulary new file mode 100644 index 0000000..f927b5d --- /dev/null +++ b/jDttSP/command-vocabulary @@ -0,0 +1,54 @@ +[TRX] indicates optional arg (RX or TX), RX default +T|F indicates TRUE or FALSE +(see enums.m4) + +setANF T|F // on/off, RX only +setANFvals taps delay gain leak // int, int, float, float, RX only +setATTOffset val // float, RX only, appears only in squelch calc +setBIN T|F // binaural mode, on/off, RX only +setFilter low-freq high-freq TRX +setFinished // shutdown gracefully +setGainOffset // float, RX only, appears only in squelch calc +setMeterOffset lev // float, RX only, appears only in squelch calc +setMode mode [TRX] // mode = USB, LSB, CWL, CWU, etc. +setNB T|F // on/off, RX only +setNBvals thresh // float, RX only +setNR T|F // on/off, RX only +setNRvals taps delay gain leak // int, int, float, float; RX only +setOsc freq [TRX] // freq in Hz (float) +setRXAGC T|F // on/off +setRXAGC mode // mode = agcOFF, agcSLOW, etc. +setRXAGCCompression lev // float +setRXAGCHang dur // float +setRXAGCLimit lim // float +setRXEQ // f0 dB0 f1 dB1 f2 dB2 ... fN (see setTXEQ) +setRXPostScl T|F // on/off +setRXPostSclVal valQ // dB +setRXPreScl T|F // on/off +setRXPreSclVal valQ // dB +setRunState state // RUN_MUTE, RUN_PASS, RUN_PLAY +setSWCH trx [zap] // trx = RX|TX, int (always zaps at least 1) +setSampleRate rate // Hz (float) +setSpotTone T|F // turn on, off +setSpotToneVals gain freq rise fall // dB, Hz, msec, msec [-12, 700, 5, 5] +setSquelch lev // float, gain, RX only; default -30dB +setSquelchSt T|F // on/off, RX only +setTRX trx // trx = RX|TX +setTXAGC T|F // on/off +setTXAGCCompression lev // float +setTXAGCHang dur // float +setTXAGCLimit lim // float +setTXEQ // f0 dB0 f1 dB1 f2 dB2 ... fN + // typical: + // 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600 + // approximates W2IHY bandcenters +setTXPostScl T|F // on/off +setTXPostSclVal valQ // dB +setTXPreScl T|F // on/off +setTXPreSclVal valQ // dB +setTXSpeechCompression T|F // on/off +setTXSpeechCompressionGain gain // float +setcorrectIQ phase gain // int, int +setcorrectIQgain gain // int +setcorrectIQphase phase // int +setfixedAGC gain [TRX] // float diff --git a/jDttSP/common.h b/jDttSP/common.h new file mode 100644 index 0000000..0f71b86 --- /dev/null +++ b/jDttSP/common.h @@ -0,0 +1,66 @@ +/* common.h +a simple way to get all the necessary includes + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _common_h + +#define _common_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/jDttSP/complex.h b/jDttSP/complex.h new file mode 100644 index 0000000..cf66d35 --- /dev/null +++ b/jDttSP/complex.h @@ -0,0 +1,46 @@ +/* complex.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _complex_h +#define _complex_h + +typedef +struct _COMPLEX { + double re, im; +} COMPLEX; + +#define c_re(x) ((x).re) +#define c_im(x) ((x).im) + +#endif + diff --git a/jDttSP/correctIQ.c b/jDttSP/correctIQ.c new file mode 100644 index 0000000..3fc3e52 --- /dev/null +++ b/jDttSP/correctIQ.c @@ -0,0 +1,57 @@ +/* correctIQ.c + +This routine restores quadrature between arms of an analytic signal +possibly distorted by ADC hardware. + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +IQ +newCorrectIQ(REAL phaseadjustment, REAL gainadjustment) { + IQ iq = (IQ) safealloc(1, sizeof(iqstate), "IQ state"); + iq->phase = phaseadjustment; + iq->gain = gainadjustment; + return iq; +} + +void +delCorrectIQ(IQ iq) { safefree((char *) iq); } + +void +correctIQ(CXB sigbuf, IQ iq) { + int i; + for (i = 0; i < CXBhave(sigbuf); i++) { + CXBimag(sigbuf, i) += iq->phase * CXBreal(sigbuf, i); + CXBreal(sigbuf, i) *= iq->gain; + } +} diff --git a/jDttSP/correctIQ.h b/jDttSP/correctIQ.h new file mode 100644 index 0000000..0077a0a --- /dev/null +++ b/jDttSP/correctIQ.h @@ -0,0 +1,14 @@ +#ifndef _correctIQ_h +#define _correctIQ_h + +#include + +typedef struct _iqstate { + REAL phase, gain; +} *IQ, iqstate; + +extern IQ newCorrectIQ(REAL phaseadjusment, REAL gainadjustment); +extern void delCorrectIQ(IQ iq); +extern void correctIQ(CXB sigbuf, IQ iq); + +#endif diff --git a/jDttSP/crc16.c b/jDttSP/crc16.c new file mode 100644 index 0000000..a38e6c9 --- /dev/null +++ b/jDttSP/crc16.c @@ -0,0 +1,110 @@ +/* crc16.c */ + +/* CCITT 16-bit CRC table and calculation function */ + +/* $Id$ */ + +/**************************************************************************** + * @format.tab-size 4 (Plain Text/Source Code File Header) + * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) + * + * Copyright 2003 Rob Swindell - http://www.synchro.net/copyright.html + * + * 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. + * See the GNU General Public License for more details: gpl.txt or + * http://www.fsf.org/copyleft/gpl.html + * + * Anonymous FTP access to the most recent released source is available at + * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net + * + * Anonymous CVS access to the development source and modification history + * is available at cvs.synchro.net:/cvsroot/sbbs, example: + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login + * (just hit return, no password is necessary) + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src + * + * For Synchronet coding style and modification guidelines, see + * http://www.synchro.net/source.html + * + * You are encouraged to submit any modifications (preferably in Unix diff + * format) via e-mail to mods@synchro.net + * + * Note: If this box doesn't appear square, then you need to fix your tabs. + ****************************************************************************/ + +/* modifications for DttSP + Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + who can be reached by email at + ab2kt@arrl.net + or + rwmcgwier@comcast.net + + or by paper mail at + + The DTTS Microwave Society + 6 Kathleen Place + Bridgewater, NJ 08807 */ + +#include + +static unsigned short crc16tbl[] = { +0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, +0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, +0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, +0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, +0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, +0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, +0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, +0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, +0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, +0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, +0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, +0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, +0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, +0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, +0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, +0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, +0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, +0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, +0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, +0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, +0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, +0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, +0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, +0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, +0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, +0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, +0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, +0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, +0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, +0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, +0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, +0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +#define ucrc16(ch,crc) (crc16tbl[(((crc)>>8)&0xff)^(unsigned char)(ch)]^((crc)<<8)) + +unsigned short +crc16(char *data, unsigned long len) { + unsigned short crc = 0; + if (data) + while (len-- > 0) { + char d = *data++; + crc = ucrc16(d, crc); + } + return crc; +} + +#ifdef notdef +unsigned short +crc16(char *data, unsigned long len) { + unsigned short crc = 0; + unsigned long l; + if (len == 0 && data != NULL) len = strlen(data); + for (l = 0; l < len; l++) crc = ucrc16(data[l], crc); + return crc; +} +#endif diff --git a/jDttSP/crc16.h b/jDttSP/crc16.h new file mode 100644 index 0000000..a244b1c --- /dev/null +++ b/jDttSP/crc16.h @@ -0,0 +1,57 @@ +/* crc16.h */ + +/* CCITT 16-bit CRC table and calculation function header */ + +/* $Id$ */ + +/**************************************************************************** + * @format.tab-size 4 (Plain Text/Source Code File Header) + * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) + * + * Copyright 2003 Rob Swindell - http://www.synchro.net/copyright.html + * + * 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. + * See the GNU General Public License for more details: gpl.txt or + * http://www.fsf.org/copyleft/gpl.html + * + * Anonymous FTP access to the most recent released source is available at + * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net + * + * Anonymous CVS access to the development source and modification history + * is available at cvs.synchro.net:/cvsroot/sbbs, example: + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login + * (just hit return, no password is necessary) + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src + * + * For Synchronet coding style and modification guidelines, see + * http://www.synchro.net/source.html + * + * You are encouraged to submit any modifications (preferably in Unix diff + * format) via e-mail to mods@synchro.net + * + * Note: If this box doesn't appear square, then you need to fix your tabs. + ****************************************************************************/ +/* modifications for DttSP + Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + who can be reached by email at + ab2kt@arrl.net + or + rwmcgwier@comcast.net + + or by paper mail at + + The DTTS Microwave Society + 6 Kathleen Place + Bridgewater, NJ 08807 */ + +#ifndef _crc16_h +#define _crc16_h + +#include + +extern unsigned short crc16(char *data, unsigned long len); + +#endif diff --git a/jDttSP/cxops.c b/jDttSP/cxops.c new file mode 100644 index 0000000..b54dbef --- /dev/null +++ b/jDttSP/cxops.c @@ -0,0 +1,109 @@ +/* cxops.c +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +// useful constants + +COMPLEX cxzero = {0.0, 0.0}; +COMPLEX cxone = {1.0, 0.0}; +COMPLEX cxJ = {0.0, 1.0}; +COMPLEX cxminusone = {-1.0, 0.0}; +COMPLEX cxminusJ = {0.0, -1.0}; + +// scalar + +COMPLEX +Cscl(COMPLEX x, REAL a) { + COMPLEX z; + c_re(z) = c_re(x) * a; + c_im(z) = c_im(x) * a; + return z; +} + +COMPLEX +Cadd(COMPLEX x, COMPLEX y) { + COMPLEX z; + c_re(z) = c_re(x) + c_re(y); + c_im(z) = c_im(x) + c_im(y); + return z; +} + +COMPLEX +Csub(COMPLEX x, COMPLEX y) { + COMPLEX z; + c_re(z) = c_re(x) - c_re(y); + c_im(z) = c_im(x) - c_im(y); + return z; +} + +COMPLEX +Cmul(COMPLEX x, COMPLEX y) { + COMPLEX z; + c_re(z) = c_re(x) * c_re(y) - c_im(x) * c_im(y); + c_im(z) = c_im(x) * c_re(y) + c_re(x) * c_im(y); + return z; +} + +COMPLEX +Cdiv(COMPLEX x, COMPLEX y) { + REAL d = sqr(c_re(y)) + sqr(c_im(y)); + COMPLEX z; + c_re(z) = (c_re(x) * c_re(y) + c_im(x) * c_im(y)) / d; + c_im(z) = (c_re(y) * c_im(x) - c_im(y) * c_re(x)) / d; + return z; +} + +REAL +Cmag(COMPLEX z) { return sqrt(sqr(z.re) + sqr(z.im)); } + +REAL +Cabs(COMPLEX z) { return sqrt(sqr(z.re) + sqr(z.im)); } + +REAL +Csqrmag(COMPLEX z) { return sqr(z.re) + sqr(z.im); } + +COMPLEX +Cmplx(REAL x, IMAG y) { + COMPLEX z; + z.re = x, z.im = y; + return z; +} + +COMPLEX +Conjg(COMPLEX z) { return Cmplx(z.re, -z.im); } + +COMPLEX +Cexp(COMPLEX z) { + REAL r = exp(z.re); + return Cmplx(r * cos(z.im), r * sin(z.im)); +} diff --git a/jDttSP/cxops.h b/jDttSP/cxops.h new file mode 100644 index 0000000..83d2e26 --- /dev/null +++ b/jDttSP/cxops.h @@ -0,0 +1,58 @@ +/* cxops.h +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _cxops_h + +#define _cxops_h + +#include + +extern COMPLEX cxzero; +extern COMPLEX cxone; +extern COMPLEX cxJ; +extern COMPLEX cxminusone; +extern COMPLEX cxminusJ; + +extern INLINE COMPLEX Cscl(COMPLEX, REAL); +extern INLINE COMPLEX Cadd(COMPLEX, COMPLEX); +extern INLINE COMPLEX Csub(COMPLEX, COMPLEX); +extern INLINE COMPLEX Cmul(COMPLEX, COMPLEX); +extern INLINE COMPLEX Cdiv(COMPLEX, COMPLEX); +extern INLINE REAL Cmag(COMPLEX); +extern INLINE REAL Cabs(COMPLEX); +extern INLINE REAL Csqrmag(COMPLEX); +extern INLINE COMPLEX Cmplx(REAL, IMAG); +extern INLINE COMPLEX Conjg(COMPLEX); +extern INLINE COMPLEX Cexp(COMPLEX); + +#endif + diff --git a/jDttSP/datatypes.h b/jDttSP/datatypes.h new file mode 100644 index 0000000..6bc3a4e --- /dev/null +++ b/jDttSP/datatypes.h @@ -0,0 +1,58 @@ +/* datatypes.h + local definitions and aliases for our data +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _datatypes_h + +#define _datatypes_h + +#include + +typedef int BOOLEAN; +typedef double REAL; +typedef double IMAG; +typedef short SAMPLE_16t; + +#include + +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifndef INLINE +#define INLINE inline +#endif + +#include + +#endif + diff --git a/jDttSP/digitalagc.c b/jDttSP/digitalagc.c new file mode 100644 index 0000000..5d559a1 --- /dev/null +++ b/jDttSP/digitalagc.c @@ -0,0 +1,127 @@ +/* digitalagc.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +We appreciate the contributions to this subroutine by Gerald Youngblood, +AC5OG and Phil Harman, VK6APH. + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +/* + * Mode is gross agc mode: off, slow, med, fast; just info + * Hang is basic hang time + * Size is max hang time + * Over is recover period after TRX switch + * Rcov is hang value used during recovery period + * BufSize is length of sample buffer + */ + +DIGITALAGC +newDigitalAgc(int Mode, + int Hang, + int Size, + int Over, + int Rcov, + int Ramp, + int BufSize, + REAL MaxGain, + REAL Limit, + REAL CurGain, + COMPLEX *Vec) { + DIGITALAGC a = (DIGITALAGC) safealloc(1, + sizeof(digital_agc_state), + "new digital agc state"); + assert((Ramp >= 2) && (Ramp < BufSize)); + a->mode = Mode; + a->hang = Hang; + a->size = Size; + a->over = Over; + a->rcov = Rcov; + a->ramp = Ramp; + a->gain.top = MaxGain; + a->gain.lim = Limit; + a->gain.old = a->gain.now = CurGain; + a->buff = newCXB(BufSize, Vec, "agc buffer"); + memset((void *) a->hist, 0, sizeof(a->hist)); + a->indx = 0; + a->gain.fix = 1000.0; + return a; +} + +void +delDigitalAgc(DIGITALAGC a) { + if (a) { + delCXB(a->buff); + safefree((void *) a); + } +} + +void +DigitalAgc(DIGITALAGC a, int tick) { + + if (a->mode == agcOFF) { + int i; + for (i = 0; i < CXBsize(a->buff); i++) + CXBdata(a->buff, i) = Cscl(CXBdata(a->buff, i), a->gain.fix); + + } else { + int i, + k = a->indx, + hang = tick < a->over ? a->rcov : a->hang; + REAL peak = 0.0; + + for (i = 0; i < CXBsize(a->buff); i++) + peak = max(peak, Cmag(CXBdata(a->buff, i))); + + if (peak != 0) { + a->size = hang; + a->hist[k] = a->gain.lim / peak; + for (i = 1, a->gain.now = a->hist[0]; i < hang; i++) + a->gain.now = min(a->hist[i], a->gain.now); + } + a->gain.now = min(a->gain.now, a->gain.top); + + if (a->gain.now != a->gain.old) { + REAL step = (a->gain.now - a->gain.old) / (a->ramp - 1); + for (i = 0; i < a->ramp; i++) + CXBdata(a->buff, i) = Cscl(CXBdata(a->buff, i), a->gain.old + i * step); + for (; i < CXBsize(a->buff); i++) + CXBdata(a->buff, i) = Cscl(CXBdata(a->buff, i), a->gain.now); + a->gain.old = a->gain.now; + + } else + for (i = 0; i < CXBsize(a->buff); i++) + CXBdata(a->buff, i) = Cscl(CXBdata(a->buff, i), a->gain.now); + + a->indx = ++k % a->size; + } +} diff --git a/jDttSP/digitalagc.h b/jDttSP/digitalagc.h new file mode 100644 index 0000000..ae886a7 --- /dev/null +++ b/jDttSP/digitalagc.h @@ -0,0 +1,74 @@ +/* digitalagc.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _digitalagc_h +#define _digitalagc_h + +#include +#include +#include +#include + +typedef enum _agcmode { agcOFF, agcLONG, agcSLOW, agcMED, agcFAST } AGCMODE; + +#define AGCHIST (24) + +typedef +struct _digitalagc { + AGCMODE mode; + int hang, indx, over, ramp, rcov, size; + struct { + REAL fix, lim, now, old, top; + } gain; + REAL hist[AGCHIST]; + CXB buff; +} digital_agc_state, *DIGITALAGC; + +extern void delDigitalAgc(DIGITALAGC agc); + +extern DIGITALAGC +newDigitalAgc(int Mode, + int Hang, + int Size, + int Ramp, + int Over, + int Rcov, + int BufSize, + REAL MaxGain, + REAL Limit, + REAL CurGain, + COMPLEX *Vec); + +extern void DigitalAgc(DIGITALAGC agc, int tick); + +#endif diff --git a/jDttSP/enums.m4 b/jDttSP/enums.m4 new file mode 100644 index 0000000..f9669f7 --- /dev/null +++ b/jDttSP/enums.m4 @@ -0,0 +1,64 @@ +define(OFF,0)dnl +define(ON,1)dnl +define(FALSE,0)dnl +define(TRUE,1)dnl +define(F,0)dnl +define(T,1)dnl +define(FIR_Undef,0)dnl +define(FIR_Lowpass,1)dnl +define(FIR_Bandpass,2)dnl +define(FIR_Highpass,3)dnl +define(FIR_Hilbert,4)dnl +define(FIR_Bandstop,5)dnl +dnl +define(FIR_Even,0)dnl +define(FIR_Odd,1)dnl +dnl +define(MAG,0)dnl +define(DB,1)dnl +dnl +define(SIGNAL_STRENGTH,0)dnl +define(AVG_SIGNAL_STRENGTH,1)dnl +define(ADC_REAL,2)dnl +define(ADC_IMAG,3)dnl +define(AGC_GAIN,4)dnl +dnl +define(agcOFF,0)dnl +define(agcLONG,1)dnl +define(agcSLOW,2)dnl +define(agcMED,3)dnl +define(agcFAST,4)dnl +dnl +define(LSB,0)dnl +define(USB,1)dnl +define(DSB,2)dnl +define(CWL,3)dnl +define(CWU,4)dnl +define(FM,5)dnl +define(FMN,5)dnl +define(AM,6)dnl +define(PSK,7)dnl +define(SPEC,8)dnl +define(RTTY,9)dnl +define(SAM,10)dnl +define(DRM,11)dnl +dnl +define(RX,0)dnl +define(TX,1)dnl +dnl +define(RECTANGULAR_WINDOW,0)dnl +define(HANNING_WINDOW,1)dnl +define(WELCH_WINDOW,2)dnl +define(PARZEN_WINDOW,3)dnl +define(BARTLETT_WINDOW,4)dnl +define(HAMMING_WINDOW,5)dnl +define(BLACKMAN2_WINDOW,6)dnl +define(BLACKMAN3_WINDOW,7)dnl +define(BLACKMAN4_WINDOW,8)dnl +define(EXPONENTIAL_WINDOW,9)dnl +define(RIEMANN_WINDOW,10)dnl +dnl +define(RUN_MUTE,0)dnl +define(RUN_PASS,1)dnl +define(RUN_PLAY,2)dnl +define(RUN_SWCH,3)dnl diff --git a/jDttSP/fastrig.c b/jDttSP/fastrig.c new file mode 100644 index 0000000..5c4d8ff --- /dev/null +++ b/jDttSP/fastrig.c @@ -0,0 +1,259 @@ + +/**************************************************************** + * Fast Trigonometric Routines Used for Imbedded Systems * + * Programmer: Bob McGwier, IDA CCR-P, June, 2000 * + ***************************************************************/ +/* This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The author can be reached by email at + +rwmcgwier@comcast.net + +or by paper mail at + +Robert W McGwier, N4HY +64 Brooktree Road +East Windsor, NJ 08520 +*/ + +#include + +REAL +phasemod(REAL angle) { + while (angle >= TWOPI) angle -= TWOPI; + while (angle < 0.0) angle += TWOPI; + return angle; +} + +//REAL phasemod(REAL angle) +//{ + // return (angle - floor(angle/TWOPI)*TWOPI); +//} + +#if (TRIG_SPEED != 0) + +static REAL TABLE_FACTOR; +static REAL *sinT, *cosT; + +/***************************************************************************/ +/* Constant definitions */ +/***************************************************************************/ +#define TAN_MAP_RES 0.003921569 /* (smallest non-zero value in table) */ +#define RAD_PER_DEG 0.017453293 +#define TAN_MAP_SIZE 256 + +/* arctangents from 0 to pi/4 radians */ +static REAL fast_atan_table[257] = { + 0.000000e+00, 3.921549e-03, 7.842976e-03, 1.176416e-02, + 1.568499e-02, 1.960533e-02, 2.352507e-02, 2.744409e-02, + 3.136226e-02, 3.527947e-02, 3.919560e-02, 4.311053e-02, + 4.702413e-02, 5.093629e-02, 5.484690e-02, 5.875582e-02, + 6.266295e-02, 6.656816e-02, 7.047134e-02, 7.437238e-02, + 7.827114e-02, 8.216752e-02, 8.606141e-02, 8.995267e-02, + 9.384121e-02, 9.772691e-02, 1.016096e-01, 1.054893e-01, + 1.093658e-01, 1.132390e-01, 1.171087e-01, 1.209750e-01, + 1.248376e-01, 1.286965e-01, 1.325515e-01, 1.364026e-01, + 1.402496e-01, 1.440924e-01, 1.479310e-01, 1.517652e-01, + 1.555948e-01, 1.594199e-01, 1.632403e-01, 1.670559e-01, + 1.708665e-01, 1.746722e-01, 1.784728e-01, 1.822681e-01, + 1.860582e-01, 1.898428e-01, 1.936220e-01, 1.973956e-01, + 2.011634e-01, 2.049255e-01, 2.086818e-01, 2.124320e-01, + 2.161762e-01, 2.199143e-01, 2.236461e-01, 2.273716e-01, + 2.310907e-01, 2.348033e-01, 2.385093e-01, 2.422086e-01, + 2.459012e-01, 2.495869e-01, 2.532658e-01, 2.569376e-01, + 2.606024e-01, 2.642600e-01, 2.679104e-01, 2.715535e-01, + 2.751892e-01, 2.788175e-01, 2.824383e-01, 2.860514e-01, + 2.896569e-01, 2.932547e-01, 2.968447e-01, 3.004268e-01, + 3.040009e-01, 3.075671e-01, 3.111252e-01, 3.146752e-01, + 3.182170e-01, 3.217506e-01, 3.252758e-01, 3.287927e-01, + 3.323012e-01, 3.358012e-01, 3.392926e-01, 3.427755e-01, + 3.462497e-01, 3.497153e-01, 3.531721e-01, 3.566201e-01, + 3.600593e-01, 3.634896e-01, 3.669110e-01, 3.703234e-01, + 3.737268e-01, 3.771211e-01, 3.805064e-01, 3.838825e-01, + 3.872494e-01, 3.906070e-01, 3.939555e-01, 3.972946e-01, + 4.006244e-01, 4.039448e-01, 4.072558e-01, 4.105574e-01, + 4.138496e-01, 4.171322e-01, 4.204054e-01, 4.236689e-01, + 4.269229e-01, 4.301673e-01, 4.334021e-01, 4.366272e-01, + 4.398426e-01, 4.430483e-01, 4.462443e-01, 4.494306e-01, + 4.526070e-01, 4.557738e-01, 4.589307e-01, 4.620778e-01, + 4.652150e-01, 4.683424e-01, 4.714600e-01, 4.745676e-01, + 4.776654e-01, 4.807532e-01, 4.838312e-01, 4.868992e-01, + 4.899573e-01, 4.930055e-01, 4.960437e-01, 4.990719e-01, + 5.020902e-01, 5.050985e-01, 5.080968e-01, 5.110852e-01, + 5.140636e-01, 5.170320e-01, 5.199904e-01, 5.229388e-01, + 5.258772e-01, 5.288056e-01, 5.317241e-01, 5.346325e-01, + 5.375310e-01, 5.404195e-01, 5.432980e-01, 5.461666e-01, + 5.490251e-01, 5.518738e-01, 5.547124e-01, 5.575411e-01, + 5.603599e-01, 5.631687e-01, 5.659676e-01, 5.687566e-01, + 5.715357e-01, 5.743048e-01, 5.770641e-01, 5.798135e-01, + 5.825531e-01, 5.852828e-01, 5.880026e-01, 5.907126e-01, + 5.934128e-01, 5.961032e-01, 5.987839e-01, 6.014547e-01, + 6.041158e-01, 6.067672e-01, 6.094088e-01, 6.120407e-01, + 6.146630e-01, 6.172755e-01, 6.198784e-01, 6.224717e-01, + 6.250554e-01, 6.276294e-01, 6.301939e-01, 6.327488e-01, + 6.352942e-01, 6.378301e-01, 6.403565e-01, 6.428734e-01, + 6.453808e-01, 6.478788e-01, 6.503674e-01, 6.528466e-01, + 6.553165e-01, 6.577770e-01, 6.602282e-01, 6.626701e-01, + 6.651027e-01, 6.675261e-01, 6.699402e-01, 6.723452e-01, + 6.747409e-01, 6.771276e-01, 6.795051e-01, 6.818735e-01, + 6.842328e-01, 6.865831e-01, 6.889244e-01, 6.912567e-01, + 6.935800e-01, 6.958943e-01, 6.981998e-01, 7.004964e-01, + 7.027841e-01, 7.050630e-01, 7.073330e-01, 7.095943e-01, + 7.118469e-01, 7.140907e-01, 7.163258e-01, 7.185523e-01, + 7.207701e-01, 7.229794e-01, 7.251800e-01, 7.273721e-01, + 7.295557e-01, 7.317307e-01, 7.338974e-01, 7.360555e-01, + 7.382053e-01, 7.403467e-01, 7.424797e-01, 7.446045e-01, + 7.467209e-01, 7.488291e-01, 7.509291e-01, 7.530208e-01, + 7.551044e-01, 7.571798e-01, 7.592472e-01, 7.613064e-01, + 7.633576e-01, 7.654008e-01, 7.674360e-01, 7.694633e-01, + 7.714826e-01, 7.734940e-01, 7.754975e-01, 7.774932e-01, + 7.794811e-01, 7.814612e-01, 7.834335e-01, 7.853983e-01, + 7.853983e-01 +}; + +void +InitSPEEDTRIG(void) { + int i, SIZE; + TABLE_FACTOR = ONE_OVER_TWOPI * SIN_TABLE_SIZE; + SIZE = sizeof(REAL) * (SIN_TABLE_SIZE + 1); + sinT = (REAL *) malloc(SIZE + (SIZE >> 2) + 1); + /* cosT=(REAL *)malloc(SIZE); */ + for (i = 0; i < SIN_TABLE_SIZE + (SIN_TABLE_SIZE >> 2) + 1; i++) { + sinT[i] = (REAL) sin((REAL) i * TWOPI / (REAL) SIN_TABLE_SIZE); + /* cosT[i] = (REAL)cos((REAL)i * TWOPI/(REAL)SIN_TABLE_SIZE); */ + } + cosT = sinT + (SIN_TABLE_SIZE >> 2); +} + +REAL +fast_sin(REAL x) { +#if (TRIG_SPEED==2) + x = (x * TABLE_FACTOR) + 0.5; + return sinT[((int) x) & (SIN_TABLE_SIZE_M1)]; +#else + int i, ip1; + REAL frac; + x = (x * TABLE_FACTOR); + i = (int)(frac = floor(x)); + ip1 = i + 1; + frac = x - frac; + return (1.0 - frac) * sinT[i] + frac * sinT[ip1]; +#endif +} + +REAL +fast_cos(REAL x) { +#if (TRIG_SPEED==2) + x = (x * TABLE_FACTOR) + 0.5; + return cosT[((int) x) & (SIN_TABLE_SIZE - 1)]; +#else + int i, ip1; + REAL frac; + x = (x * TABLE_FACTOR); + i = (int)(frac = floor(x)); + ip1 = i + 1; + frac = x - frac; + return (1.0 - frac) * cosT[i] + frac * cosT[ip1]; +#endif +} + +/***************************************************************************** + Function: Arc tangent + + Syntax: angle = fast_atan2(y, x); + REAL y y component of input vector + REAL x x component of input vector + REAL angle angle of vector (x, y) in radians + + Description: This function calculates the angle of the vector (x,y) based + on a table lookup and linear interpolation. The table uses + a 256 point table covering -45 to +45 degrees and uses + symetry to determine the final angle value in the range of + -180 to 180 degrees. Note that this function uses the small + angle approximation for values close to zero. This routine + calculates the arc tangent with an average error of + +/- 0.045 degrees. +*****************************************************************************/ + +REAL +fast_atan2(REAL y, REAL x) { + REAL x_abs, y_abs, z; + REAL alpha, angle, base_angle; + int index; + + /* don't divide by zero! */ + if ((y == 0.0) && (x == 0.0)) angle = 0.0; + else { + /* normalize to +/- 45 degree range */ + y_abs = fabs(y); + x_abs = fabs(x); + z = (y_abs < x_abs ? y_abs / x_abs : x_abs / y_abs); + /* when ratio approaches the table resolution, the angle is */ + /* best approximated with the argument itself... */ + if (z < TAN_MAP_RES) base_angle = z; + else { + /* find index and interpolation value */ + alpha = z * (REAL) TAN_MAP_SIZE - .5; + index = (int) alpha; + alpha -= (REAL) index; + /* determine base angle based on quadrant and */ + /* add or subtract table value from base angle based on quadrant */ + base_angle = fast_atan_table[index]; + base_angle += + (fast_atan_table[index + 1] - fast_atan_table[index]) * alpha; + } + + if (x_abs > y_abs) { /* -45 -> 45 or 135 -> 225 */ + if (x >= 0.0) { /* -45 -> 45 */ + if (y >= 0.0) + angle = base_angle; /* 0 -> 45, angle OK */ + else + angle = -base_angle; /* -45 -> 0, angle = -angle */ + } else { /* 135 -> 180 or 180 -> -135 */ + angle = 3.14159265358979323846; + if (y >= 0.0) + angle -= base_angle; /* 135 -> 180, angle = 180 - angle */ + else + angle = base_angle - angle; /* 180 -> -135, angle = angle - 180 */ + } + } else { /* 45 -> 135 or -135 -> -45 */ + if (y >= 0.0) { /* 45 -> 135 */ + angle = 1.57079632679489661923; + if (x >= 0.0) + angle -= base_angle; /* 45 -> 90, angle = 90 - angle */ + else + angle += base_angle; /* 90 -> 135, angle = 90 + angle */ + } else { /* -135 -> -45 */ + angle = -1.57079632679489661923; + if (x >= 0.0) + angle += base_angle; /* -90 -> -45, angle = -90 + angle */ + else + angle -= base_angle; /* -135 -> -90, angle = -90 - angle */ + } + } + } +#ifdef ZERO_TO_TWOPI + if (angle < 0) return (angle + TWOPI); + else return (angle); +#else + return (angle); +#endif +} + +#endif diff --git a/jDttSP/fastrig.h b/jDttSP/fastrig.h new file mode 100644 index 0000000..ae8485b --- /dev/null +++ b/jDttSP/fastrig.h @@ -0,0 +1,99 @@ +/* fastrig.h +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _fastrig_h +#define _fastrig_h + +#include +#include +#include +#include +#include +#include + +#define SIN_TABLE_SIZE 4096 +#define SIN_TABLE_SIZE_M1 4095 + +/* ********************************************** + * TRIG_SPEED: + * 0 = normal (slow); + * 1 = table look up with interpolation (medium); + * 2 = table look up (fast) + * ***********************************************/ + +/* Interpolation is ALWAYS done on atan2. The setting + only applies to sin and cos */ + +#ifndef TRIG_SPEED +#define TRIG_SPEED 0 +#endif + +#if (TRIG_SPEED == 2) +#define SIN(x) fast_sin(x) +#define COS(x) fast_cos(x) +#define ATAN2(x,y) fast_atan2((x),(y)) + +#elif (TRIG_SPEED == 1) + +#define SIN(x) fast_sin(x) +#define COS(x) fast_cos(x) +#define ATAN2(x,y) fast_atan2((x),(y)) + +#elif (TRIG_SPEED == 0) + +#define SIN(x) (REAL)sin((REAL)x) +#define COS(x) (REAL)cos((REAL)x) +#define ATAN2(x,y) (REAL)atan2((REAL)(x),(REAL)(y)) + +#endif + +#ifndef PI +#define PI M_PI +#endif /* PI */ + +#ifndef TWOPI +#define TWOPI (2.0 * PI) +#endif + +#ifndef ONE_OVER_TWOPI +#define ONE_OVER_TWOPI (0.159154943091895) +#endif + +#if (TRIG_SPEED != 0) + +extern void InitSPEEDTRIG(void); +extern REAL fast_sin(REAL); +extern REAL fast_cos(REAL); +extern REAL fast_atan2(REAL, REAL); + +#endif +#endif diff --git a/jDttSP/fftw.h b/jDttSP/fftw.h new file mode 100644 index 0000000..9384082 --- /dev/null +++ b/jDttSP/fftw.h @@ -0,0 +1,426 @@ +/* -*- C -*- */ +/* + * Copyright (c) 1997-1999, 2003 Massachusetts Institute of Technology + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* fftw.h -- system-wide definitions */ +/* $Id$ */ + +#ifndef FFTW_H +#define FFTW_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define for using single precision */ +/* + * If you can, use configure --enable-float instead of changing this + * flag directly + */ +/* #undef FFTW_ENABLE_FLOAT */ + +/* our real numbers */ +#ifdef FFTW_ENABLE_FLOAT +typedef float fftw_real; +#else +typedef double fftw_real; +#endif + +/********************************************* + * Complex numbers and operations + *********************************************/ +typedef struct { + fftw_real re, im; +} fftw_complex; + +#ifndef c_re +#define c_re(c) ((c).re) +#endif +#ifndef c_im +#define c_im(c) ((c).im) +#endif + +typedef enum { + FFTW_FORWARD = -1, FFTW_BACKWARD = 1 +} fftw_direction; + +/* backward compatibility with FFTW-1.3 */ +typedef fftw_complex FFTW_COMPLEX; +typedef fftw_real FFTW_REAL; + +#ifndef FFTW_1_0_COMPATIBILITY +#define FFTW_1_0_COMPATIBILITY 0 +#endif + +#if FFTW_1_0_COMPATIBILITY +/* backward compatibility with FFTW-1.0 */ +#define REAL fftw_real +#define COMPLEX fftw_complex +#endif + +/********************************************* + * Success or failure status + *********************************************/ + +typedef enum { + FFTW_SUCCESS = 0, FFTW_FAILURE = -1 +} fftw_status; + +/********************************************* + * Codelets + *********************************************/ +typedef void (fftw_notw_codelet) + (const fftw_complex *, fftw_complex *, int, int); +typedef void (fftw_twiddle_codelet) + (fftw_complex *, const fftw_complex *, int, + int, int); +typedef void (fftw_generic_codelet) + (fftw_complex *, const fftw_complex *, int, + int, int, int); +typedef void (fftw_real2hc_codelet) + (const fftw_real *, fftw_real *, fftw_real *, + int, int, int); +typedef void (fftw_hc2real_codelet) + (const fftw_real *, const fftw_real *, + fftw_real *, int, int, int); +typedef void (fftw_hc2hc_codelet) + (fftw_real *, const fftw_complex *, + int, int, int); +typedef void (fftw_rgeneric_codelet) + (fftw_real *, const fftw_complex *, int, + int, int, int); + +/********************************************* + * Configurations + *********************************************/ +/* + * A configuration is a database of all known codelets + */ + +enum fftw_node_type { + FFTW_NOTW, FFTW_TWIDDLE, FFTW_GENERIC, FFTW_RADER, + FFTW_REAL2HC, FFTW_HC2REAL, FFTW_HC2HC, FFTW_RGENERIC +}; + +/* description of a codelet */ +typedef struct { + const char *name; /* name of the codelet */ + void (*codelet) (); /* pointer to the codelet itself */ + int size; /* size of the codelet */ + fftw_direction dir; /* direction */ + enum fftw_node_type type; /* TWIDDLE or NO_TWIDDLE */ + int signature; /* unique id */ + int ntwiddle; /* number of twiddle factors */ + const int *twiddle_order; /* + * array that determines the order + * in which the codelet expects + * the twiddle factors + */ +} fftw_codelet_desc; + +/* On Win32, you need to do funny things to access global variables + in shared libraries. Thanks to Andrew Sterian for this hack. */ +#ifdef HAVE_WIN32 +# if defined(BUILD_FFTW_DLL) +# define DL_IMPORT(type) __declspec(dllexport) type +# elif defined(USE_FFTW_DLL) +# define DL_IMPORT(type) __declspec(dllimport) type +# else +# define DL_IMPORT(type) type +# endif +#else +# define DL_IMPORT(type) type +#endif + +extern DL_IMPORT(const char *) fftw_version; + +/***************************** + * Plans + *****************************/ +/* + * A plan is a sequence of reductions to compute a FFT of + * a given size. At each step, the FFT algorithm can: + * + * 1) apply a notw codelet, or + * 2) recurse and apply a twiddle codelet, or + * 3) apply the generic codelet. + */ + +/* structure that contains twiddle factors */ +typedef struct fftw_twiddle_struct { + int n; + const fftw_codelet_desc *cdesc; + fftw_complex *twarray; + struct fftw_twiddle_struct *next; + int refcnt; +} fftw_twiddle; + +typedef struct fftw_rader_data_struct { + struct fftw_plan_struct *plan; + fftw_complex *omega; + int g, ginv; + int p, flags, refcount; + struct fftw_rader_data_struct *next; + fftw_codelet_desc *cdesc; +} fftw_rader_data; + +typedef void (fftw_rader_codelet) + (fftw_complex *, const fftw_complex *, int, + int, int, fftw_rader_data *); + +/* structure that holds all the data needed for a given step */ +typedef struct fftw_plan_node_struct { + enum fftw_node_type type; + + union { + /* nodes of type FFTW_NOTW */ + struct { + int size; + fftw_notw_codelet *codelet; + const fftw_codelet_desc *codelet_desc; + } notw; + + /* nodes of type FFTW_TWIDDLE */ + struct { + int size; + fftw_twiddle_codelet *codelet; + fftw_twiddle *tw; + struct fftw_plan_node_struct *recurse; + const fftw_codelet_desc *codelet_desc; + } twiddle; + + /* nodes of type FFTW_GENERIC */ + struct { + int size; + fftw_generic_codelet *codelet; + fftw_twiddle *tw; + struct fftw_plan_node_struct *recurse; + } generic; + + /* nodes of type FFTW_RADER */ + struct { + int size; + fftw_rader_codelet *codelet; + fftw_rader_data *rader_data; + fftw_twiddle *tw; + struct fftw_plan_node_struct *recurse; + } rader; + + /* nodes of type FFTW_REAL2HC */ + struct { + int size; + fftw_real2hc_codelet *codelet; + const fftw_codelet_desc *codelet_desc; + } real2hc; + + /* nodes of type FFTW_HC2REAL */ + struct { + int size; + fftw_hc2real_codelet *codelet; + const fftw_codelet_desc *codelet_desc; + } hc2real; + + /* nodes of type FFTW_HC2HC */ + struct { + int size; + fftw_direction dir; + fftw_hc2hc_codelet *codelet; + fftw_twiddle *tw; + struct fftw_plan_node_struct *recurse; + const fftw_codelet_desc *codelet_desc; + } hc2hc; + + /* nodes of type FFTW_RGENERIC */ + struct { + int size; + fftw_direction dir; + fftw_rgeneric_codelet *codelet; + fftw_twiddle *tw; + struct fftw_plan_node_struct *recurse; + } rgeneric; + } nodeu; + + int refcnt; +} fftw_plan_node; + +typedef enum { + FFTW_NORMAL_RECURSE = 0, + FFTW_VECTOR_RECURSE = 1 +} fftw_recurse_kind; + +struct fftw_plan_struct { + int n; + int refcnt; + fftw_direction dir; + int flags; + int wisdom_signature; + enum fftw_node_type wisdom_type; + struct fftw_plan_struct *next; + fftw_plan_node *root; + double cost; + fftw_recurse_kind recurse_kind; + int vector_size; +}; + +typedef struct fftw_plan_struct *fftw_plan; + +/* flags for the planner */ +#define FFTW_ESTIMATE (0) +#define FFTW_MEASURE (1) + +#define FFTW_OUT_OF_PLACE (0) +#define FFTW_IN_PLACE (8) +#define FFTW_USE_WISDOM (16) + +#define FFTW_THREADSAFE (128) /* guarantee plan is read-only so that the + same plan can be used in parallel by + multiple threads */ + +#define FFTWND_FORCE_BUFFERED (256) /* internal flag, forces buffering + in fftwnd transforms */ + +#define FFTW_NO_VECTOR_RECURSE (512) /* internal flag, prevents use + of vector recursion */ + +extern fftw_plan fftw_create_plan_specific(int n, fftw_direction dir, + int flags, + fftw_complex *in, int istride, + fftw_complex *out, int ostride); +#define FFTW_HAS_PLAN_SPECIFIC +extern fftw_plan fftw_create_plan(int n, fftw_direction dir, int flags); +extern void fftw_print_plan(fftw_plan plan); +extern void fftw_destroy_plan(fftw_plan plan); +extern void fftw(fftw_plan plan, int howmany, fftw_complex *in, int istride, + int idist, fftw_complex *out, int ostride, int odist); +extern void fftw_one(fftw_plan plan, fftw_complex *in, fftw_complex *out); +extern void fftw_die(const char *s); +extern void *fftw_malloc(size_t n); +extern void fftw_free(void *p); +extern void fftw_check_memory_leaks(void); +extern void fftw_print_max_memory_usage(void); + +typedef void *(*fftw_malloc_type_function) (size_t n); +typedef void (*fftw_free_type_function) (void *p); +typedef void (*fftw_die_type_function) (const char *errString); +extern DL_IMPORT(fftw_malloc_type_function) fftw_malloc_hook; +extern DL_IMPORT(fftw_free_type_function) fftw_free_hook; +extern DL_IMPORT(fftw_die_type_function) fftw_die_hook; + +extern size_t fftw_sizeof_fftw_real(void); + +/* Wisdom: */ +/* + * define this symbol so that users know we are using a version of FFTW + * with wisdom + */ +#define FFTW_HAS_WISDOM +extern void fftw_forget_wisdom(void); +extern void fftw_export_wisdom(void (*emitter) (char c, void *), void *data); +extern fftw_status fftw_import_wisdom(int (*g) (void *), void *data); +extern void fftw_export_wisdom_to_file(FILE *output_file); +extern fftw_status fftw_import_wisdom_from_file(FILE *input_file); +extern char *fftw_export_wisdom_to_string(void); +extern fftw_status fftw_import_wisdom_from_string(const char *input_string); + +/* + * define symbol so we know this function is available (it is not in + * older FFTWs) + */ +#define FFTW_HAS_FPRINT_PLAN +extern void fftw_fprint_plan(FILE *f, fftw_plan plan); + +/***************************** + * N-dimensional code + *****************************/ +typedef struct { + int is_in_place; /* 1 if for in-place FFTs, 0 otherwise */ + + int rank; /* + * the rank (number of dimensions) of the + * array to be FFTed + */ + int *n; /* + * the dimensions of the array to the + * FFTed + */ + fftw_direction dir; + + int *n_before; /* + * n_before[i] = product of n[j] for j < i + */ + int *n_after; /* n_after[i] = product of n[j] for j > i */ + + fftw_plan *plans; /* 1d fftw plans for each dimension */ + + int nbuffers, nwork; + fftw_complex *work; /* + * work array big enough to hold + * nbuffers+1 of the largest dimension + * (has nwork elements) + */ +} fftwnd_data; + +typedef fftwnd_data *fftwnd_plan; + +/* Initializing the FFTWND plan: */ +extern fftwnd_plan fftw2d_create_plan(int nx, int ny, fftw_direction dir, + int flags); +extern fftwnd_plan fftw3d_create_plan(int nx, int ny, int nz, + fftw_direction dir, int flags); +extern fftwnd_plan fftwnd_create_plan(int rank, const int *n, + fftw_direction dir, + int flags); + +extern fftwnd_plan fftw2d_create_plan_specific(int nx, int ny, + fftw_direction dir, + int flags, + fftw_complex *in, int istride, + fftw_complex *out, int ostride); +extern fftwnd_plan fftw3d_create_plan_specific(int nx, int ny, int nz, + fftw_direction dir, int flags, + fftw_complex *in, int istride, + fftw_complex *out, int ostride); +extern fftwnd_plan fftwnd_create_plan_specific(int rank, const int *n, + fftw_direction dir, + int flags, + fftw_complex *in, int istride, + fftw_complex *out, int ostride); + +/* Freeing the FFTWND plan: */ +extern void fftwnd_destroy_plan(fftwnd_plan plan); + +/* Printing the plan: */ +extern void fftwnd_fprint_plan(FILE *f, fftwnd_plan p); +extern void fftwnd_print_plan(fftwnd_plan p); +#define FFTWND_HAS_PRINT_PLAN + +/* Computing the N-Dimensional FFT */ +extern void fftwnd(fftwnd_plan plan, int howmany, + fftw_complex *in, int istride, int idist, + fftw_complex *out, int ostride, int odist); +extern void fftwnd_one(fftwnd_plan p, fftw_complex *in, fftw_complex *out); + +#ifdef __cplusplus +} /* extern "C" */ + +#endif /* __cplusplus */ +#endif /* FFTW_H */ diff --git a/jDttSP/filter.c b/jDttSP/filter.c new file mode 100644 index 0000000..a9bca9c --- /dev/null +++ b/jDttSP/filter.c @@ -0,0 +1,504 @@ +/* filter.c +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +static REAL onepi = 3.141592653589793; + +#define twopi TWOPI + +RealFIR +newFIR_REAL(int size, char *tag) { + RealFIR p = (RealFIRDesc *) safealloc(1, sizeof(RealFIRDesc), tag); + FIRcoef(p) = (REAL *) safealloc(size, sizeof(REAL), tag); + FIRsize(p) = size; + FIRtype(p) = FIR_Undef; + FIRiscomplex(p) = FALSE; + FIRfqlo(p) = FIRfqhi(p) = -1.0; + return p; +} + +ComplexFIR +newFIR_COMPLEX(int size, char *tag) { + ComplexFIR p = (ComplexFIRDesc *) safealloc(1, sizeof(ComplexFIRDesc), tag); + FIRcoef(p) = (COMPLEX *) safealloc(size, sizeof(COMPLEX), tag); + FIRsize(p) = size; + FIRtype(p) = FIR_Undef; + FIRiscomplex(p) = TRUE; + FIRfqlo(p) = FIRfqhi(p) = -1.0; + return p; +} + +void +delFIR_REAL(RealFIR p) { + if (p) { + delvec_REAL(FIRcoef(p)); + free((void *) p); + } +} + +void +delFIR_COMPLEX(ComplexFIR p) { + if (p) { + delvec_COMPLEX(FIRcoef(p)); + free((void *) p); + } +} + +RealFIR +newFIR_Lowpass_REAL(REAL cutoff, REAL sr, int size) { + if ((cutoff < 0.0) || (cutoff > (sr / 2.0))) return 0; + else if (size < 1) return 0; + else { + RealFIR p; + REAL *h, *w, fc = cutoff / sr; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_REAL(size, "newFIR_Lowpass_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Lowpass_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j] = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1] = 2.0 * fc; + } + + delvec_REAL(w); + FIRtype(p) = FIR_Lowpass; + return p; + } +} + +ComplexFIR +newFIR_Lowpass_COMPLEX(REAL cutoff, REAL sr, int size) { + if ((cutoff < 0.0) || (cutoff > (sr / 2.0))) return 0; + else if (size < 1) return 0; + else { + ComplexFIR p; + COMPLEX *h; + REAL *w, fc = cutoff / sr; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_COMPLEX(size, "newFIR_Lowpass_COMPLEX"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Lowpass_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j].re = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1].re = 2.0 * fc; + } + + delvec_REAL(w); + FIRtype(p) = FIR_Lowpass; + return p; + } +} + +RealFIR +newFIR_Bandpass_REAL(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < 0.0) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + RealFIR p; + REAL *h, *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_REAL(size, "newFIR_Bandpass_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Bandpass_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j] = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1] = 2.0 * fc; + h[j] *= 2.0 * cos(ff * (i - midpoint)); + } + + delvec_REAL(w); + FIRtype(p) = FIR_Bandpass; + return p; + } +} + +ComplexFIR +newFIR_Bandpass_COMPLEX(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < -(sr/2.0)) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + ComplexFIR p; + COMPLEX *h; + REAL *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_COMPLEX(size, "newFIR_Bandpass_COMPLEX"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Bandpass_COMPLEX window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1, k = i - midpoint; + REAL tmp, phs = ff * k; + if (i != midpoint) + tmp = (sin(twopi * k * fc) / (onepi * k)) * w[j]; + else + tmp = 2.0 * fc; + tmp *= 2.0; + h[j].re = tmp * cos(phs); + h[j].im = tmp * sin(phs); + } + + delvec_REAL(w); + FIRtype(p) = FIR_Bandpass; + return p; + } +} + +RealFIR +newFIR_Highpass_REAL(REAL cutoff, REAL sr, int size) { + if ((cutoff < 0.0) || (cutoff > (sr / 2.0))) return 0; + else if (size < 1) return 0; + else { + RealFIR p; + REAL *h, *w, fc = cutoff / sr; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_REAL(size, "newFIR_Highpass_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Highpass_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j] = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1] = 2.0 * fc; + } + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) h[j] = -h[j]; + else h[midpoint - 1] = 1.0 - h[midpoint - 1]; + } + + delvec_REAL(w); + FIRtype(p) = FIR_Highpass; + return p; + } +} + +ComplexFIR +newFIR_Highpass_COMPLEX(REAL cutoff, REAL sr, int size) { + if ((cutoff < 0.0) || (cutoff > (sr / 2.0))) return 0; + else if (size < 1) return 0; + else { + ComplexFIR p; + COMPLEX *h; + REAL *w, fc = cutoff / sr; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_COMPLEX(size, "newFIR_Highpass_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Highpass_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j].re = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1].re = 2.0 * fc; + } + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) h[j].re = -h[j].re; + else h[midpoint - 1].re = 1.0 - h[midpoint - 1].re; + } + + delvec_REAL(w); + FIRtype(p) = FIR_Highpass; + return p; + } +} + +RealFIR +newFIR_Hilbert_REAL(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < 0.0) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + RealFIR p; + REAL *h, *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_REAL(size, "newFIR_Hilbert_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Hilbert_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j] = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1] = 2.0 * fc; + h[j] *= 2.0 * sin(ff * (i - midpoint)); + } + + delvec_REAL(w); + FIRtype(p) = FIR_Hilbert; + return p; + } +} + +ComplexFIR +newFIR_Hilbert_COMPLEX(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < 0.0) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + ComplexFIR p; + COMPLEX *h; + REAL *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_COMPLEX(size, "newFIR_Hilbert_COMPLEX"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Hilbert_COMPLEX window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1; + REAL tmp, phs = ff * (i - midpoint); + if (i != midpoint) + tmp = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + tmp = 2.0 * fc; + tmp *= 2.0; + /* h[j].re *= tmp * cos(phs); */ + h[j].im *= tmp * sin(phs); + } + + delvec_REAL(w); + FIRtype(p) = FIR_Hilbert; + return p; + } +} + +RealFIR +newFIR_Bandstop_REAL(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < 0.0) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + RealFIR p; + REAL *h, *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_REAL(size, "newFIR_Bandstop_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Bandstop_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) + h[j] = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + h[midpoint - 1] = 2.0 * fc; + h[j] *= 2.0 * cos(ff * (i - midpoint)); + } + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) h[j] = -h[j]; + else h[midpoint - 1] = 1.0 - h[midpoint - 1]; + } + + delvec_REAL(w); + FIRtype(p) = FIR_Bandstop; + return p; + } +} + +ComplexFIR +newFIR_Bandstop_COMPLEX(REAL lo, REAL hi, REAL sr, int size) { + if ((lo < 0.0) || (hi > (sr / 2.0)) || (hi <= lo)) return 0; + else if (size < 1) return 0; + else { + ComplexFIR p; + COMPLEX *h; + REAL *w, fc, ff; + int i, midpoint; + + if (!(size & 01)) size++; + midpoint = (size >> 01) | 01; + p = newFIR_COMPLEX(size, "newFIR_Bandstop_REAL"); + h = FIRcoef(p); + w = newvec_REAL(size, "newFIR_Bandstop_REAL window"); + (void) makewindow(BLACKMANHARRIS_WINDOW, size, w); + + lo /= sr, hi /= sr; + fc = (hi - lo) / 2.0; + ff = (lo + hi) * onepi; + + for (i = 1; i <= size; i++) { + int j = i - 1; + REAL tmp, phs = ff * (i - midpoint); + if (i != midpoint) + tmp = (sin(twopi * (i - midpoint) * fc) / (onepi * (i - midpoint))) * w[j]; + else + tmp = 2.0 * fc; + tmp *= 2.0; + h[j].re *= tmp * cos(phs); + h[j].im *= tmp * sin(phs); + } + + for (i = 1; i <= size; i++) { + int j = i - 1; + if (i != midpoint) h[j] = Cmul(h[j], cxminusone); + else h[midpoint - 1] = Csub(cxone, h[midpoint - 1]); + } + + delvec_REAL(w); + FIRtype(p) = FIR_Bandstop; + return p; + } +} + +#ifdef notdef +int +main(int argc, char **argv) { + int i, size = 101; + RealFIR filt; +#ifdef notdef + filt = newFIR_Lowpass_REAL(1000.0, 48000.0, size); + for (i = 0; i < FIRsize(filt); i++) + printf("%d %f\n", i, FIRtap(filt, i)); + delFIR_REAL(filt); +#endif + +#ifdef notdef + filt = newFIR_Bandstop_REAL(1000.0, 2000.0, 48000.0, size); + for (i = 0; i < FIRsize(filt); i++) + printf("%d %f\n", i, FIRtap(filt, i)); + delFIR_REAL(filt); +#endif + + filt = newFIR_Bandpass_REAL(1000.0, 2000.0, 48000.0, size); + for (i = 0; i < FIRsize(filt); i++) + printf("%d %f\n", i, FIRtap(filt, i)); + delFIR_REAL(filt); + +#ifdef notdef + filt = newFIR_Highpass_REAL(1000.0, 48000.0, size); + for (i = 0; i < FIRsize(filt); i++) + printf("%d %f\n", i, FIRtap(filt, i)); + delFIR_REAL(filt); +#endif + +#ifdef notdef + filt = newFIR_Hilbert_REAL(1000.0, 2000.0, 48000.0, size); + for (i = 0; i < FIRsize(filt); i++) + printf("%d %f\n", i, FIRtap(filt, i)); + delFIR_REAL(filt); +#endif + +#ifdef notdef + { + COMPLEX *z; + REAL *ttbl; + int fftlen; + fftlen = nblock2(size) * 2; + z = newvec_COMPLEX(fftlen, "z"); + ttbl = newvec_REAL(fftlen, "ttbl"); + cfftm(FFT_INIT, fftlen, (float *) z, (float *) z, ttbl); + for (i = 0; i < FIRsize(filt); i++) + z[i].re = FIRtap(filt, i); + cfftm(FFT_FORWARD, fftlen, (float *) z, (float *) z, ttbl); + for (i = 0; i < size; i++) { + printf("%d %f\n", i, Cabs(z[i])); + delvec_COMPLEX(z); + delvec_REAL(ttbl); + } + } +#endif + exit(0); +} +#endif diff --git a/jDttSP/filter.h b/jDttSP/filter.h new file mode 100644 index 0000000..47eb97e --- /dev/null +++ b/jDttSP/filter.h @@ -0,0 +1,108 @@ +/* filter.h +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _filter_h + +#define _filter_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + FIR_Undef, FIR_Lowpass, FIR_Bandpass, FIR_Highpass, FIR_Hilbert, FIR_Bandstop +} FIR_response_type; + +typedef enum { FIR_Even, FIR_Odd } FIR_parity_type; + +typedef +struct _real_FIR { + REAL *coef; + int size; + FIR_response_type type; + BOOLEAN cplx; + struct { REAL lo, hi; } freq; +} RealFIRDesc, *RealFIR; + +typedef +struct _complex_FIR { + COMPLEX *coef; + int size; + FIR_response_type type; + BOOLEAN cplx; + struct { REAL lo, hi; } freq; +} ComplexFIRDesc, *ComplexFIR; + +#define FIRcoef(p) ((p)->coef) +#define FIRtap(p, i) (FIRcoef(p)[(i)]) +#define FIRsize(p) ((p)->size) +#define FIRtype(p) ((p)->type) +#define FIRiscomplex(p) ((p)->cplx) +#define FIRisreal(p) (!FIRiscomplex(p)) +#define FIRfqlo(p) ((p)->freq.lo) +#define FIRfqhi(p) ((p)->freq.hi) + +#define delFIR_Lowpass_REAL(p) delFIR_REAL(p) +#define delFIR_Lowpass_COMPLEX(p) delFIR_COMPLEX(p) +#define delFIR_Bandpass_REAL(p) delFIR_REAL(p) +#define delFIR_Bandpass_COMPLEX(p) delFIR_COMPLEX(p) +#define delFIR_Highpass_REAL(p) delFIR_REAL(p) +#define delFIR_Highpass_COMPLEX(p) delFIR_COMPLEX(p) +#define delFIR_Hilbert_REAL(p) delFIR_REAL(p) +#define delFIR_Hilbert_COMPLEX(p) delFIR_COMPLEX(p) +#define delFIR_Bandstop_REAL(p) delFIR_REAL(p) +#define delFIR_Bandstop_COMPLEX(p) delFIR_COMPLEX(p) + +extern RealFIR newFIR_REAL(int size, char *tag); +extern ComplexFIR newFIR_COMPLEX(int size, char *tag); +extern void delFIR_REAL(RealFIR p); +extern void delFIR_COMPLEX(ComplexFIR p); +extern RealFIR newFIR_Lowpass_REAL(REAL cutoff, REAL sr, int size); +extern ComplexFIR newFIR_Lowpass_COMPLEX(REAL cutoff, REAL sr, int size); +extern RealFIR newFIR_Bandpass_REAL(REAL lo, REAL hi, REAL sr, int size); +extern ComplexFIR newFIR_Bandpass_COMPLEX(REAL lo, REAL hi, REAL sr, int size); +extern RealFIR newFIR_Highpass_REAL(REAL cutoff, REAL sr, int size); +extern ComplexFIR newFIR_Highpass_COMPLEX(REAL cutoff, REAL sr, int size); +extern RealFIR newFIR_Hilbert_REAL(REAL lo, REAL hi, REAL sr, int size); +extern ComplexFIR newFIR_Hilbert_COMPLEX(REAL lo, REAL hi, REAL sr, int size); +extern RealFIR newFIR_Bandstop_REAL(REAL lo, REAL hi, REAL sr, int size); +extern ComplexFIR newFIR_Bandstop_COMPLEX(REAL lo, REAL hi, REAL sr, int size); + +#endif diff --git a/jDttSP/fm_demod.c b/jDttSP/fm_demod.c new file mode 100644 index 0000000..1735eaf --- /dev/null +++ b/jDttSP/fm_demod.c @@ -0,0 +1,103 @@ +/* fm_demod.c */ + +#include + +/*------------------------------------------------------------------------------*/ +/* private to FM */ +/*------------------------------------------------------------------------------*/ + +PRIVATE void +init_pll(FMD fm, + REAL samprate, + REAL freq, + REAL lofreq, + REAL hifreq, + REAL bandwidth) { + REAL fac = TWOPI / samprate; + + fm->pll.freq.f = freq * fac; + fm->pll.freq.l = lofreq * fac; + fm->pll.freq.h = hifreq * fac; + fm->pll.phs = 0.0; + fm->pll.delay = cxJ; + + fm->pll.iir.alpha = bandwidth * fac; /* arm filter */ + fm->pll.alpha = fm->pll.iir.alpha * 0.3; /* pll bandwidth */ + fm->pll.beta = fm->pll.alpha * fm->pll.alpha * 0.25; /* second order term */ +} + +PRIVATE void +pll(FMD fm, COMPLEX sig) { + COMPLEX z = Cmplx(cos(fm->pll.phs), sin(fm->pll.phs)); + REAL diff; + + fm->pll.delay.re = z.re * sig.re + z.im * sig.im; + fm->pll.delay.im = -z.im * sig.re + z.re * sig.im; + diff = ATAN2(fm->pll.delay.im, fm->pll.delay.re); + + fm->pll.freq.f += fm->pll.beta * diff; + + if (fm->pll.freq.f < fm->pll.freq.l) + fm->pll.freq.f = fm->pll.freq.l; + if (fm->pll.freq.f > fm->pll.freq.h) + fm->pll.freq.f = fm->pll.freq.h; + + fm->pll.phs += fm->pll.freq.f + fm->pll.alpha * diff; + + while (fm->pll.phs >= TWOPI) fm->pll.phs -= TWOPI; + while (fm->pll.phs < 0) fm->pll.phs += TWOPI; +} + +/*------------------------------------------------------------------------------*/ +/* public */ +/*------------------------------------------------------------------------------*/ + +void +FMDemod(FMD fm) { + int i; + for (i = 0; i < CXBsize(fm->ibuf); i++) { + pll(fm, CXBdata(fm->ibuf, i)); + fm->afc = 0.9999 * fm->afc + 0.0001 * fm->pll.freq.f; + CXBreal(fm->obuf, i) = + CXBimag(fm->obuf, i) = (fm->pll.freq.f - fm->afc) * fm->cvt; + } +} + +FMD +newFMD(REAL samprate, + REAL f_initial, + REAL f_lobound, + REAL f_hibound, + REAL f_bandwid, + int size, + COMPLEX *ivec, + COMPLEX *ovec, + char *tag) { + FMD fm = (FMD) safealloc(1, sizeof(FMDDesc), tag); + + fm->size = size; + fm->ibuf = newCXB(size, ivec, tag); + fm->obuf = newCXB(size, ovec, tag); + + init_pll(fm, + samprate, + f_initial, + f_lobound, + f_hibound, + f_bandwid); + + fm->lock = 0.5; + fm->afc = 0.0; + fm->cvt = 0.45*samprate/(M_PI*f_bandwid); + + return fm; +} + +void +delFMD(FMD fm) { + if (fm) { + delCXB(fm->ibuf); + delCXB(fm->obuf); + safefree((char *) fm); + } +} diff --git a/jDttSP/fm_demod.h b/jDttSP/fm_demod.h new file mode 100644 index 0000000..136ba78 --- /dev/null +++ b/jDttSP/fm_demod.h @@ -0,0 +1,55 @@ +/* fm_demod.h */ + +#ifndef _fm_demod_h +#define _fm_demod_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef +struct _fm_demod { + int size; + CXB ibuf, obuf; + struct { + REAL alpha, beta; + struct { REAL f, l, h; } freq; + REAL phs; + struct { REAL alpha; } iir; + COMPLEX delay; + } pll; + + REAL lock, afc, cvt; +} FMDDesc, *FMD; + +extern void FMDemod(FMD fm); +extern FMD newFMD(REAL samprate, + REAL f_initial, + REAL f_lobound, + REAL f_hibound, + REAL f_bandwid, + int size, + COMPLEX *ivec, + COMPLEX *ovec, + char *tag); +extern void delFMD(FMD fm); + +#ifndef TWOPI +#define TWOPI (2.0*M_PI) +#endif +#ifndef CvtMod2Freq +#define CvtMod2Freq 0.25 +#endif + +#endif diff --git a/jDttSP/fromsys.h b/jDttSP/fromsys.h new file mode 100644 index 0000000..56e6a8f --- /dev/null +++ b/jDttSP/fromsys.h @@ -0,0 +1,65 @@ +/* fromsys.h + stuff we need to import everywhere + This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _fromsys_h +#define _fromsys_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#endif diff --git a/jDttSP/lmadf.c b/jDttSP/lmadf.c new file mode 100644 index 0000000..c472ec4 --- /dev/null +++ b/jDttSP/lmadf.c @@ -0,0 +1,286 @@ +/* lmadf.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +#ifdef REALLMS + +LMSR +new_lmsr(CXB signal, + int delay, + REAL adaptation_rate, + REAL leakage, + int adaptive_filter_size, + int filter_type) { + LMSR lms = (LMSR) safealloc(1, sizeof(_lmsstate), "new_lmsr state"); + + lms->signal = signal; + CXBsize(lms->signal); + lms->delay = delay; + lms->size = 512; + lms->mask = lms->size - 1; + lms->delay_line = newvec_REAL(lms->size, "lmsr delay"); + lms->adaptation_rate = adaptation_rate; + lms->leakage = leakage; + lms->adaptive_filter_size = adaptive_filter_size; + lms->adaptive_filter = newvec_REAL(128, "lmsr filter"); + lms->filter_type = filter_type; + lms->delay_line_ptr = 0; + + return lms; +} + +void +del_lmsr(LMSR lms) { + if (lms) { + delvec_REAL(lms->delay_line); + delvec_REAL(lms->adaptive_filter); + safefree((char *) lms); + } +} + +// just to make the algorithm itself a little clearer, +// get the admin stuff out of the way + +#define ssiz (lms->signal_size) +#define asiz (lms->adaptive_filter_size) +#define dptr (lms->delay_line_ptr) +#define rate (lms->adaptation_rate) +#define leak (lms->leakage) + +#define ssig(n) (CXBreal(lms->signal,(n))) + +#define dlay(n) (lms->delay_line[(n)]) + +#define afil(n) (lms->adaptive_filter[(n)]) +#define wrap(n) (((n) + (lms->delay) + (lms->delay_line_ptr)) & (lms->mask)) +#define bump(n) (((n) + (lms->mask)) & (lms->mask)) + +static void +lmsr_adapt_i(LMSR lms) { + int i, j, k; + REAL sum_sq, scl1, scl2; + REAL accum, error; + + scl1 = 1.0 - rate * leak; + + for (i = 0; i < ssiz; i++) { + + dlay(dptr) = ssig(i); + accum = 0.0; + sum_sq = 0.0; + + for (j = 0; j < asiz; j++) { + k = wrap(j); + sum_sq += sqr(dlay(k)); + accum += afil(j) * dlay(k); + } + + error = ssig(i) - accum; + ssig(i) = error; + + scl2 = rate / (sum_sq + 1e-10); + error *= scl2; + for (j = 0; j < asiz; j++) { + k = wrap(j); + afil(j) = afil(j) * scl1 + error * dlay(k); + } + + dptr = bump(dptr); + } +} + +static void +lmsr_adapt_n(LMSR lms) { + int i, j, k; + REAL sum_sq, scl1, scl2; + REAL accum, error; + + scl1 = 1.0 - rate * leak; + + for (i = 0; i < ssiz; i++) { + + dlay(dptr) = ssig(i); + accum = 0.0; + sum_sq = 0.0; + + for (j = 0; j < asiz; j++) { + k = wrap(j); + sum_sq += sqr(dlay(k)); + accum += afil(j) * dlay(k); + } + + error = ssig(i) - accum; + ssig(i) = accum; + + scl2 = rate / (sum_sq + 1e-10); + error *= scl2; + for (j = 0; j < asiz; j++) { + k = wrap(j); + afil(j) = afil(j) * scl1 + error * dlay(k); + } + + dptr = bump(dptr); + } +} + +#else + +LMSR +new_lmsr(CXB signal, + int delay, + REAL adaptation_rate, + REAL leakage, + int adaptive_filter_size, + int filter_type) { + LMSR lms = (LMSR) safealloc(1, sizeof(_lmsstate), "new_lmsr state"); + + lms->signal = signal; + CXBsize(lms->signal); + lms->delay = delay; + lms->size = 512; + lms->mask = lms->size - 1; + lms->delay_line = newvec_COMPLEX(lms->size, "lmsr delay"); + lms->adaptation_rate = adaptation_rate; + lms->leakage = leakage; + lms->adaptive_filter_size = adaptive_filter_size; + lms->adaptive_filter = newvec_COMPLEX(128, "lmsr filter"); + lms->filter_type = filter_type; + lms->delay_line_ptr = 0; + + return lms; +} + +void +del_lmsr(LMSR lms) { + if (lms) { + delvec_COMPLEX(lms->delay_line); + delvec_COMPLEX(lms->adaptive_filter); + safefree((char *) lms); + } +} + +// just to make the algorithm itself a little clearer, +// get the admin stuff out of the way + +#define ssiz (lms->signal_size) +#define asiz (lms->adaptive_filter_size) +#define dptr (lms->delay_line_ptr) +#define rate (lms->adaptation_rate) +#define leak (lms->leakage) + +#define ssig(n) (CXBdata(lms->signal,(n))) + +#define dlay(n) (lms->delay_line[(n)]) + +#define afil(n) (lms->adaptive_filter[(n)]) +#define wrap(n) (((n) + (lms->delay) + (lms->delay_line_ptr)) & (lms->mask)) +#define bump(n) (((n) + (lms->mask)) & (lms->mask)) + +static void +lmsr_adapt_i(LMSR lms) { + int i, j, k; + REAL sum_sq, scl1, scl2; + COMPLEX accum, error; + scl1 = 1.0 - rate * leak; + for (i = 0; i < ssiz; i++) { + + dlay(dptr) = ssig(i); + accum = cxzero; + sum_sq = 0.0; + + for (j = 0; j < asiz; j++) { + k = wrap(j); + sum_sq += Csqrmag(dlay(k)); + accum = Cadd(accum, Cmul(Conjg(afil(j)), dlay(k))); + } + + error = Csub(ssig(i), accum); + ssig(i) = error; + + scl2 = rate / (sum_sq + 1e-10); + error = Cscl(Conjg(error),scl2); + for (j = 0; j < asiz; j++) { + k = wrap(j); + afil(j) = Cadd(Cscl(afil(j), scl1),Cmul(error, dlay(k))); + } + + dptr = bump(dptr); + } +} + +static void +lmsr_adapt_n(LMSR lms) { + int i, j, k; + REAL sum_sq, scl1, scl2; + COMPLEX accum, error; + scl1 = 1.0 - rate * leak; + for (i = 0; i < ssiz; i++) { + + dlay(dptr) = ssig(i); + accum = cxzero; + sum_sq = 0.0; + + for (j = 0; j < asiz; j++) { + k = wrap(j); + sum_sq += Csqrmag(dlay(k)); + accum = Cadd(accum, Cmul(Conjg(afil(j)), dlay(k))); + } + + error = Csub(ssig(i), accum); + ssig(i) = accum; + + scl2 = rate / (sum_sq + 1e-10); + error = Cscl(Conjg(error),scl2); + for (j = 0; j < asiz; j++) { + k = wrap(j); + afil(j) = Cadd(Cscl(afil(j), scl1),Cmul(error, dlay(k))); + } + + dptr = bump(dptr); + } +} + +#endif + +extern void +lmsr_adapt(LMSR lms) { + switch(lms->filter_type) { + case LMADF_NOISE: + lmsr_adapt_n(lms); + break; + case LMADF_INTERFERENCE: + lmsr_adapt_i(lms); + break; + } +} diff --git a/jDttSP/lmadf.h b/jDttSP/lmadf.h new file mode 100644 index 0000000..a3566a9 --- /dev/null +++ b/jDttSP/lmadf.h @@ -0,0 +1,101 @@ +/* lmadf.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _lmadf_h + +#define _lmadf_h + +#include +#include +#include +#include +#include + +#define LMADF_INTERFERENCE 0 +#define LMADF_NOISE 1 +#define LMADF_NOSIG (-1) +#define LMADF_NOLINE (-2) +#define LMADF_NOFILT (-3) + +extern int lmadf_err; + +#define REALLMS + +#ifdef REALLMS + +typedef struct _LMSR { + CXB signal; /* Signal Buffer */ + int signal_size; /* Number of samples in signal buffer */ + REAL *delay_line; /* Delay Line circular buffer for holding samples */ + REAL *adaptive_filter; /* Filter coefficients */ + REAL adaptation_rate; /* Adaptation rate for the LMS stochastic gradient */ + REAL leakage; /* Exponential decay constant for filter coefficients */ + int adaptive_filter_size; /* number taps in adaptive filter */ + int filter_type; /* Filter type */ + int delay; /* Total delay between current sample and filter */ + int delay_line_ptr; /* Pointer for next sample into the delay line */ + int size; /* Delay line size */ + int mask; /* Mask for circular buffer */ +} *LMSR, _lmsstate; + +#else + +typedef struct _LMSR { + CXB signal; /* Signal Buffer */ + int signal_size; /* Number of samples in signal buffer */ + COMPLEX *delay_line; /* Delay Line circular buffer for holding samples */ + COMPLEX *adaptive_filter; /* Filter coefficients */ + REAL adaptation_rate; /* Adaptation rate for the LMS stochastic gradient */ + REAL leakage; /* Exponential decay constant for filter coefficients */ + int adaptive_filter_size; /* number taps in adaptive filter */ + int filter_type; /* Filter type */ + int delay; /* Total delay between current sample and filter */ + int delay_line_ptr; /* Pointer for next sample into the delay line */ + int size; /* Delay line size */ + int mask; /* Mask for circular buffer */ +} *LMSR, _lmsstate; + +#endif + +extern LMSR new_lmsr(CXB signal, + int delay, + REAL adaptation_rate, + REAL leakage, + int adaptive_filter_size, + int filter_type); + +extern void del_lmsr(LMSR lms); + +extern void lmsr_adapt(LMSR lms); + +#endif diff --git a/jDttSP/local.h b/jDttSP/local.h new file mode 100644 index 0000000..942fc2b --- /dev/null +++ b/jDttSP/local.h @@ -0,0 +1,77 @@ +/* local.h + +Some manifest constants for the particular implementation + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ + +#include + +#ifndef _local_h +#define _local_h + +#define RCBASE ".DttSPrc" +#define PARMPATH "./IPC/SDR-1000-0-commands.fifo" +#define METERPATH "./IPC/SDR-1000-0-meter.chan" +#define WISDOMPATH "./wisdom" +#define RINGMULT (4) +#define METERMULT (2) +#define DEFRATE (48000.0) +#define DEFSIZE (2048) +#define DEFMODE (SAM) + +#ifndef MAXPATHLEN +#define MAXPATHLEN 2048 +#endif + +extern struct _loc { + char name[MAXPATHLEN]; + struct { + char rcfile[MAXPATHLEN], + parm[MAXPATHLEN], + meter[MAXPATHLEN], + wisdom[MAXPATHLEN]; + } path; + struct { + REAL rate; + int size; + SDRMODE mode; + } def; + struct { int ring, meter; } mult; +} loc; + +#endif diff --git a/jDttSP/main.c b/jDttSP/main.c new file mode 100644 index 0000000..a4611e3 --- /dev/null +++ b/jDttSP/main.c @@ -0,0 +1,528 @@ +/* main.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +///////////////////////////////////////////////////////////////////////// +// elementary defaults + +struct _loc loc; + +///////////////////////////////////////////////////////////////////////// +// most of what little we know here about the inner loop, +// functionally speaking + +extern void process_samples(float *, float *, int); +extern void setup_workspace(void); +extern void destroy_workspace(void); + +//======================================================================== + +PRIVATE void +monitor_thread(void) { + while (top.running) { + sem_wait(&top.sync.mon.sem); + fprintf(stderr, + "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n", + uni.tick, + top.jack.blow.cb, + top.jack.blow.rb.i, + top.jack.blow.rb.o, + top.jack.blow.xr); + memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow)); + } + pthread_exit(0); +} + +//======================================================================== + +PRIVATE void +process_updates_thread(void) { + while (top.running) { + pthread_testcancel(); + while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp)) + do_update(top.parm.buff, top.verbose ? stderr : 0); + } + pthread_exit(0); +} + +PRIVATE void +gethold(void) { + if (jack_ringbuffer_write_space(top.jack.ring.o.l) + < top.hold.size.bytes) { + // pathology + jack_ringbuffer_reset(top.jack.ring.o.l); + jack_ringbuffer_reset(top.jack.ring.o.r); + top.jack.blow.rb.o++; + } + jack_ringbuffer_write(top.jack.ring.o.l, + (char *) top.hold.buf.l, + top.hold.size.bytes); + jack_ringbuffer_write(top.jack.ring.o.r, + (char *) top.hold.buf.r, + top.hold.size.bytes); + if (jack_ringbuffer_read_space(top.jack.ring.i.l) + < top.hold.size.bytes) { + // pathology + jack_ringbuffer_reset(top.jack.ring.i.l); + jack_ringbuffer_reset(top.jack.ring.i.r); + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + top.jack.blow.rb.i++; + } else { + jack_ringbuffer_read(top.jack.ring.i.l, + (char *) top.hold.buf.l, + top.hold.size.bytes); + jack_ringbuffer_read(top.jack.ring.i.r, + (char *) top.hold.buf.r, + top.hold.size.bytes); + } +} + +PRIVATE BOOLEAN +canhold(void) { + return + jack_ringbuffer_read_space(top.jack.ring.i.l) + >= top.hold.size.bytes; +} + +PRIVATE void +run_mute(void) { + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + uni.tick++; +} + +PRIVATE void +run_pass(void) { uni.tick++; } + +PRIVATE void +run_play(void) { + process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames); +} + +// NB do not set RUN_SWCH directly via setRunState; +// use setSWCH instead + +PRIVATE void +run_swch(void) { + if (top.swch.bfct.have == 0) { + // first time + // apply ramp down + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) 1.0 - (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + memset((char *) (top.hold.buf.l + m), 0, n); + memset((char *) (top.hold.buf.r + m), 0, n); + top.swch.bfct.have++; + } else if (top.swch.bfct.have < top.swch.bfct.want) { + // in medias res + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + top.swch.bfct.have++; + } else { + // last time + // apply ramp up + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + uni.mode.trx = top.swch.trx.next; + rx.tick = tx.tick = 0; + top.state = top.swch.run.last; + top.swch.bfct.want = top.swch.bfct.have = 0; + } + + process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames); +} + +//======================================================================== + +void +clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) { + int i; + char zero = 0; + for (i = 0; i < nbytes; i++) + jack_ringbuffer_write(rb, &zero, 1); +} + +PRIVATE void +audio_callback(jack_nframes_t nframes, void *arg) { + float *lp, *rp; + int nbytes = nframes * sizeof(float); + + if (nframes == top.jack.size) { + + // output: copy from ring to port + lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes); + rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes); + + if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) { + jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes); + jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes); + } else { // rb pathology + memset((char *) lp, 0, nbytes); + memset((char *) rp, 0, nbytes); + jack_ringbuffer_reset(top.jack.ring.o.l); + jack_ringbuffer_reset(top.jack.ring.o.r); + clear_jack_ringbuffer(top.jack.ring.o.l, nbytes); + clear_jack_ringbuffer(top.jack.ring.o.r, nbytes); + top.jack.blow.rb.o++; + } + + // input: copy from port to ring + if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) { + lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes); + rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes); + jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes); + jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes); + } else { // rb pathology + jack_ringbuffer_reset(top.jack.ring.i.l); + jack_ringbuffer_reset(top.jack.ring.i.r); + clear_jack_ringbuffer(top.jack.ring.i.l, nbytes); + clear_jack_ringbuffer(top.jack.ring.i.r, nbytes); + top.jack.blow.rb.i++; + } + + } else { // callback pathology + jack_ringbuffer_reset(top.jack.ring.i.l); + jack_ringbuffer_reset(top.jack.ring.i.r); + jack_ringbuffer_reset(top.jack.ring.o.l); + jack_ringbuffer_reset(top.jack.ring.o.r); + clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes); + clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes); + top.jack.blow.cb++; + } + + // if enough accumulated in ring, fire dsp + if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes) + sem_post(&top.sync.buf.sem); + + // check for blowups + if ((top.jack.blow.cb > 0) || + (top.jack.blow.rb.i > 0) || + (top.jack.blow.rb.o > 0)) + sem_post(&top.sync.mon.sem); +} + +PRIVATE void +process_samples_thread(void) { + while (top.running) { + sem_wait(&top.sync.buf.sem); + do { + gethold(); + sem_wait(&top.sync.upd.sem); + switch (top.state) { + case RUN_MUTE: run_mute(); break; + case RUN_PASS: run_pass(); break; + case RUN_PLAY: run_play(); break; + case RUN_SWCH: run_swch(); break; + } + sem_post(&top.sync.upd.sem); + } while (canhold()); + } + pthread_exit(0); +} + +//======================================================================== + +PRIVATE void +execute(void) { + // let updates run + sem_post(&top.sync.upd.sem); + + // rcfile + { + FILE *frc = find_rcfile(loc.path.rcfile); + if (frc) { + while (fgets(top.parm.buff, sizeof(top.parm.buff), frc)) + do_update(top.parm.buff, top.verbose ? stderr : 0); + fclose(frc); + } + } + + // start audio processing + if (jack_activate(top.jack.client)) + perror("cannot activate jack client"), exit(1); + + // wait for threads to terminate + pthread_join(top.thrd.trx.id, 0); + pthread_join(top.thrd.upd.id, 0); + pthread_join(top.thrd.mon.id, 0); + + // stop audio processing + jack_client_close(top.jack.client); +} + +PRIVATE void +closeup(void) { + jack_ringbuffer_free(top.jack.ring.o.r); + jack_ringbuffer_free(top.jack.ring.o.l); + jack_ringbuffer_free(top.jack.ring.i.r); + jack_ringbuffer_free(top.jack.ring.i.l); + + safefree((char *) top.hold.buf.r); + safefree((char *) top.hold.buf.l); + + destroy_workspace(); + + exit(0); +} + +PRIVATE void +usage(void) { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "jsdr [-flag [arg]] [file]\n"); + fprintf(stderr, "flags:\n"); + fprintf(stderr, " -v verbose commentary\n"); + fprintf(stderr, " -m do metering\n"); + fprintf(stderr, " -l file execute update commands in file at startup\n"); + fprintf(stderr, " -P cmdpath path to command/update pipe\n"); + fprintf(stderr, " -S s-mtrpath path to S-meter output channel\n"); + fprintf(stderr, " -W wispath path to FFTW wisdom file\n"); + fprintf(stderr, " -R rate sampling rate\n"); + fprintf(stderr, " -B bufsize internal DSP buffer size\n"); + fprintf(stderr, " -M mode start up in mode (SAM, USB, LCW, etc.)\n"); + fprintf(stderr, " -G num use num as ringbuffer mult\n"); + fprintf(stderr, " -E num use num as meter chan mult\n"); + fprintf(stderr, "'file' arg unused, but available\n"); + exit(1); +} + +PRIVATE void +nonblock(int fd) { + long arg; + arg = fcntl(fd, F_GETFL); + arg |= O_NONBLOCK; +/* if (fcntl(fd, F_GETFL, &arg) >= 0) + fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ + fcntl(fd, F_SETFL, arg); +} + +//........................................................................ + +PRIVATE void +setup_switching(void) { + top.swch.fade = (int) (0.1 * uni.buflen + 0.5); + top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float); +} + +PRIVATE void +setup_local_audio(void) { + top.hold.size.frames = uni.buflen; + top.hold.size.bytes = top.hold.size.frames * sizeof(float); + top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer left"); + top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer right"); +} + +PRIVATE void +setup_updates(void) { + top.parm.path = loc.path.parm; + if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1) + perror(top.parm.path), exit(1); + if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) { + fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm); + exit(1); + } +} + +PRIVATE void +jack_xrun(void *arg) { + top.jack.blow.xr++; + sem_post(&top.sync.mon.sem); +} + +PRIVATE void +jack_shutdown(void *arg) {} + +PRIVATE void +setup_system_audio(void) { + if (loc.name[0]) strcpy(top.jack.name, loc.name); + else sprintf(top.jack.name, "sdr-%d", top.pid); + if (!(top.jack.client = jack_client_new(top.jack.name))) + perror("can't make client -- jack not running?"), exit(1); + + jack_set_process_callback(top.jack.client, (void *) audio_callback, 0); + jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0); + jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0); + top.jack.size = jack_get_buffer_size(top.jack.client); + memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow)); + + top.jack.port.i.l = jack_port_register(top.jack.client, + "il", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + top.jack.port.i.r = jack_port_register(top.jack.client, + "ir", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + top.jack.port.o.l = jack_port_register(top.jack.client, + "ol", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + top.jack.port.o.r = jack_port_register(top.jack.client, + "or", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); + top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); + top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); + top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); + clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes); + clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes); +} + +PRIVATE void +setup_threading(void) { + sem_init(&top.sync.upd.sem, 0, 0); + pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL); + sem_init(&top.sync.buf.sem, 0, 0); + pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL); + sem_init(&top.sync.mon.sem, 0, 0); + pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL); +} + +//======================================================================== +// hard defaults, then environment + +PRIVATE void +setup_defaults(void) { + loc.name[0] = 0; // no default name for jack client + strcpy(loc.path.rcfile, RCBASE); + strcpy(loc.path.parm, PARMPATH); + strcpy(loc.path.meter, METERPATH); + strcpy(loc.path.wisdom, WISDOMPATH); + loc.def.rate = DEFRATE; + loc.def.size = DEFSIZE; + loc.def.mode = DEFMODE; + loc.mult.ring = RINGMULT; + loc.mult.meter = METERMULT; + + { + char *ep; + if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep); + if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep); + if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep); + if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep); + if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep); + if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep); + if ((ep = getenv("SDR_METERMULT"))) loc.mult.meter = atoi(ep); + if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep); + if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep); + if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep); + } +} + +//======================================================================== +PRIVATE void +setup(int argc, char **argv) { + int i; + + top.uid = getuid(); + top.pid = getpid(); + top.start_tv = now_tv(); + top.running = TRUE; + top.verbose = FALSE; + top.state = RUN_PLAY; + + setup_defaults(); + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'v': + top.verbose = TRUE; + break; + case 'l': + strcpy(loc.path.rcfile, argv[++i]); + break; + case 'm': + uni.meter.flag = TRUE; + break; + case 'P': + strcpy(loc.path.parm, argv[++i]); + break; + case 'S': + strcpy(loc.path.meter, argv[++i]); + break; + case 'W': + strcpy(loc.path.wisdom, argv[++i]); + break; + case 'R': + loc.def.rate = atof(argv[++i]); + break; + case 'B': + loc.def.size = atoi(argv[++i]); + break; + case 'M': + loc.def.mode = atoi(argv[++i]); + break; + case 'G': + loc.mult.ring = atoi(argv[++i]); + break; + case 'E': + loc.mult.meter = atoi(argv[++i]); + break; + default: + usage(); + } + else break; + if (i < argc) { + if (!freopen(argv[i], "r", stdin)) + perror(argv[i]), exit(1); + i++; + } + + setup_workspace(); + setup_updates(); + + setup_local_audio(); + setup_system_audio(); + + setup_threading(); + setup_switching(); +} + +//======================================================================== + +int +main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); } diff --git a/jDttSP/meter.h b/jDttSP/meter.h new file mode 100644 index 0000000..57e7b71 --- /dev/null +++ b/jDttSP/meter.h @@ -0,0 +1,12 @@ +#ifndef _meter_h +#define _meter_h + +typedef enum { + SIGNAL_STRENGTH, + AVG_SIGNAL_STRENGTH, + ADC_REAL, + ADC_IMAG, + AGC_GAIN +} METERTYPE; + +#endif diff --git a/jDttSP/metermon.c b/jDttSP/metermon.c new file mode 100644 index 0000000..1bd875b --- /dev/null +++ b/jDttSP/metermon.c @@ -0,0 +1,44 @@ +/* metermon.c */ + +#include + +#define METERPATH "./IPC/SDR-1000-0-meter.chan" +#define METERMULT (24) +#define SLEEP (500000) + +jmp_buf here; + +void +onsig(int sig) { + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + longjmp(here, TRUE); +} + +int +main(int argc, char **argv) { + Chan ch = 0; + int i = 0; + REAL val = 0.0; + + signal(SIGHUP, onsig); + signal(SIGINT, onsig); + signal(SIGQUIT, onsig); + + if (!(ch = openChan(METERPATH, METERMULT * sizeof(REAL)))) + perror("openChan"), exit(1); + + while (!setjmp(here)) { + if (getChan_nowait(ch, (char *) &val, sizeof(REAL))) { + printf("(%d)", i++); + do + printf(" %f", val); + while (getChan_nowait(ch, (char *) &val, sizeof(REAL))); + putchar('\n'); + } + usleep(SLEEP); + } + + closeChan(ch); +} diff --git a/jDttSP/mkchan.c b/jDttSP/mkchan.c new file mode 100644 index 0000000..2855980 --- /dev/null +++ b/jDttSP/mkchan.c @@ -0,0 +1,84 @@ +/* mkchan.c */ +/* create a file big enough to accommodate + a header + a ringbuffer of a given size */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +char *rng_name = 0, + *buf_asks = 0; +FILE *rng_file = 0; +size_t rng_size = 0, + buf_size = 0, + blk_size = 0, + tot_size = 0; + +BOOLEAN verbose = FALSE; + +void +execute(void) { + int i; + rng_size = sizeof(ringb_t); + if ((buf_size = atoi(buf_asks)) <= 0) { + fprintf(stderr, "buffer size %s?\n", buf_asks); + exit(1); + } + if (!(rng_file = fopen(rng_name, "w"))) { + perror(rng_name); + exit(1); + } + blk_size = nblock2(buf_size); + tot_size = rng_size + blk_size; + for (i = 0; i < tot_size; i++) putc(0, rng_file); + fclose(rng_file); + if (verbose) + fprintf(stderr, + "created chan file %s (%d + [%d -> %d] = %d)\n", + rng_name, rng_size, buf_size, blk_size, tot_size); +} + +void +closeup(void) { exit(0); } + +static void +usage(void) { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "mkchan [-v] name size\n"); + exit(1); +} + +static void +setup(int argc, char **argv) { + int i; + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'v': verbose = TRUE; break; + default: usage(); + } + else break; + if (i < (argc - 2)) usage(); + rng_name = argv[i++]; + buf_asks = argv[i++]; +} + +int +main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); } diff --git a/jDttSP/noiseblanker.c b/jDttSP/noiseblanker.c new file mode 100644 index 0000000..406ebaa --- /dev/null +++ b/jDttSP/noiseblanker.c @@ -0,0 +1,72 @@ +/* noiseblanker.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +NB +new_noiseblanker(CXB sigbuf, REAL threshold) { + NB nb = (NB) safealloc(1, sizeof(nbstate), "new nbstate"); + nb->sigbuf = sigbuf; + nb->threshold = threshold; + nb->average_mag = 1.0; + return nb; +} + +void +del_nb(NB nb) { + if (nb) { safefree((char *) nb); } +} + +void +noiseblanker(NB nb) { + int i; + for (i = 0; i < CXBsize(nb->sigbuf); i++) { + REAL cmag = Cmag(CXBdata(nb->sigbuf, i)); + nb->average_mag = 0.999 * (nb->average_mag) + 0.001 * cmag; + if (cmag > (nb->threshold * nb->average_mag)) + CXBdata(nb->sigbuf, i) = Cmplx(0.0, 0.0); + } +} + +void +SDROMnoiseblanker(NB nb) { + int i; + for (i = 0; i < CXBsize(nb->sigbuf); i++) { + REAL cmag = Cmag(CXBdata(nb->sigbuf, i)); + nb->average_sig = Cadd(Cscl(nb->average_sig, 0.75), + Cscl(CXBdata(nb->sigbuf, i), 0.25)); + nb->average_mag = 0.999 * (nb->average_mag) + 0.001 * cmag; + if (cmag > (nb->threshold * nb->average_mag)) + CXBdata(nb->sigbuf, i) = nb->average_sig; + } +} diff --git a/jDttSP/noiseblanker.h b/jDttSP/noiseblanker.h new file mode 100644 index 0000000..8270ed2 --- /dev/null +++ b/jDttSP/noiseblanker.h @@ -0,0 +1,51 @@ +/* update.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _noiseblanker_h +#define _noiseblanker_h + +#include +#include + +typedef struct _nbstate { + CXB sigbuf; /* Signal Buffer */ + REAL threshold; /* Noise Blanker Threshold */ + COMPLEX average_sig; + REAL average_mag; +} *NB, nbstate; + +extern NB new_noiseblanker(CXB sigbuf, REAL threshold); +extern void del_nb(NB nb); +extern void noiseblanker(NB nb); +extern void SDROMnoiseblanker(NB nb); +#endif diff --git a/jDttSP/oscillator.c b/jDttSP/oscillator.c new file mode 100644 index 0000000..04c0b03 --- /dev/null +++ b/jDttSP/oscillator.c @@ -0,0 +1,117 @@ +/* oscillator.c + +This routine implements a common fixed-frequency oscillator + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +#define HUGE_PHASE 1256637061.43593 + +void +ComplexOSC(OSC p) { + int i; + COMPLEX z, delta_z; + + if (OSCphase(p) > HUGE_PHASE) OSCphase(p) -= HUGE_PHASE; + + z = Cmplx(cos(OSCphase(p)), sin(OSCphase(p))), + delta_z = Cmplx(cos(OSCfreq(p)), sin(OSCfreq(p))); + + for (i = 0; i < OSCsize(p); i++) + z = CXBdata((CXB) OSCbase(p), i) + = Cmul(z, delta_z), + OSCphase(p) += OSCfreq(p); +} + +#ifdef notdef +void +ComplexOSC(OSC p) { + int i; + if (OSCphase(p) > 1256637061.43593) + OSCphase(p) -= 1256637061.43593; + for (i = 0; i < OSCsize(p); i++) { + OSCreal(p, i) = cos(OSCphase(p)); + OSCimag(p, i) = sin(OSCphase(p)); + OSCphase(p) += OSCfreq(p); + } +} +#endif + +PRIVATE REAL +_phasemod(REAL angle) { + while (angle >= TWOPI) angle -= TWOPI; + while (angle < 0.0) angle += TWOPI; + return angle; +} + +void +RealOSC(OSC p) { + int i; + for (i = 0; i < OSCsize(p); i++) { + OSCRdata(p, i) = sin(OSCphase(p)); + OSCphase(p) = _phasemod(OSCfreq(p) + OSCphase(p)); + } +} + +OSC +newOSC(int size, + OscType TypeOsc, + REAL Frequency, + REAL Phase, + REAL SampleRate, + char *tag) { + OSC p = (OSC) safealloc(1, sizeof(oscillator), tag); + if ((OSCtype(p) = TypeOsc) == ComplexTone) + OSCbase(p) = (void *) newCXB(size, + NULL, + "complex buffer for oscillator output"); + else + OSCbase(p) = (void *) newRLB(size, + NULL, + "real buffer for oscillator output"); + OSCsize(p) = size; + OSCfreq(p) = 2.0 * M_PI * Frequency / SampleRate; + OSCphase(p) = Phase; + return p; +} + +void +delOSC(OSC p) { + if (p->OscillatorType == ComplexTone) + delCXB((CXB) p->signalpoints); + else + delRLB((RLB) p->signalpoints); + if (p) + safefree((char *) p); +} + diff --git a/jDttSP/oscillator.h b/jDttSP/oscillator.h new file mode 100644 index 0000000..7f45233 --- /dev/null +++ b/jDttSP/oscillator.h @@ -0,0 +1,72 @@ +/* oscillator.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + + +#ifndef _oscillator_h +#define _oscillator_h + +#define ComplexTone 1 +#define RealTone 0 +typedef int OscType; + +typedef struct _oscillator { + int size; + void *signalpoints; + REAL Phase; + REAL Frequency; + OscType OscillatorType; +} oscillator, *OSC; + + +#define OSCbase(p) ((p)->signalpoints) +#define OSCCbase(p) (CXBbase((CXB)((p)->signalpoints))) +#define OSCCdata(p, i) (CXBbase((CXB)((p)->signalpoints))[(i)]) +#define OSCreal(p, i) (CXBbase((CXB)((p)->signalpoints))[(i)].re) +#define OSCimag(p, i) (CXBbase((CXB)((p)->signalpoints))[(i)].im) + +#define OSCRbase(p) (RLBbase((RLB)((p)->signalpoints))) +#define OSCRdata(p, i) (RLBbase((RLB)((p)->signalpoints))[(i)]) + +#define OSCsize(p) ((p)->size) +#define OSCphase(p) ((p)->Phase) +#define OSCfreq(p) ((p)->Frequency) +#define OSCtype(p) ((p)->OscillatorType) + +extern void ComplexOSC(OSC); +extern void RealOSC(OSC); +extern OSC newOSC(int size, OscType TypeOsc, REAL Frequency, REAL Phase, + REAL SampleRate, char *tag); +extern void delOSC(OSC); +extern void fixOSC(OSC p, REAL Frequency, REAL Phase, REAL SampleRate); + +#endif diff --git a/jDttSP/ovsv.c b/jDttSP/ovsv.c new file mode 100644 index 0000000..3575ff4 --- /dev/null +++ b/jDttSP/ovsv.c @@ -0,0 +1,220 @@ +/* ovsv.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +/*------------------------------------------------------------*/ +/* run the filter */ + +void +filter_OvSv(FiltOvSv pflt) { + int i, + j, + m = pflt->fftlen, + n = pflt->buflen; + COMPLEX *zfvec = pflt->zfvec, + *zivec = pflt->zivec, + *zovec = pflt->zovec, + *zrvec = pflt->zrvec; + REAL scl = pflt->scale; + + /* input sig -> z */ + fftw_one(pflt->pfwd, (fftw_complex *) zrvec, (fftw_complex *) zivec); + + /* convolve in z */ + for (i = 0; i < m; i++) zivec[i] = Cmul(zivec[i], zfvec[i]); + + /* z convolved sig -> time output sig */ + fftw_one(pflt->pinv, (fftw_complex *) zivec, (fftw_complex *) zovec); + + /* scale */ + for (i = 0; i < n; i++) zovec[i].re *= scl, zovec[i].im *= scl; + + /* prepare input sig vec for next fill */ + for (i = 0, j = n; i < n; i++, j++) zrvec[i] = zrvec[j]; +} + +void +filter_OvSv_par(FiltOvSv pflt) { + int i, + j, + m = pflt->fftlen, + n = pflt->buflen; + COMPLEX *zfvec = pflt->zfvec, + *zivec = pflt->zivec, + *zovec = pflt->zovec, + *zrvec = pflt->zrvec; + REAL scl = pflt->scale; + + /* input sig -> z */ + fftw_one(pflt->pfwd,(fftw_complex *) zrvec, (fftw_complex *) zivec); + + /* convolve in z */ + for (i = 0; i < m; i++) zivec[i] = Cmul(zivec[i], zfvec[i]); + + /* z convolved sig -> time output sig */ + fftw_one(pflt->pinv, (fftw_complex *) zivec, (fftw_complex *) zovec); + + /* scale */ + for (i = 0; i < n; i++) zovec[i].re *= scl, zovec[i].im *= scl; + + /* prepare input sig vec for next fill */ + for (i = 0, j = n; i < n; i++, j++) zrvec[i] = zrvec[j]; +} + +/*------------------------------------------------------------*/ +/* info: */ +/* NB strategy. This is the address we pass to newCXB as + the place to read samples into. It's the right half of + the true buffer. Old samples get slid from here to + left half after each go-around. */ +COMPLEX * +FiltOvSv_initpoint(FiltOvSv pflt) { return &(pflt->zrvec[pflt->buflen]); } + +/* how many to put there */ +int +FiltOvSv_initsize(FiltOvSv pflt) { return (pflt->fftlen - pflt->buflen); } + +/* where to put next batch of samples to filter */ +COMPLEX * +FiltOvSv_fetchpoint(FiltOvSv pflt) { return &(pflt->zrvec[pflt->buflen]); } + +/* how many samples to put there */ + +int +FiltOvSv_fetchsize(FiltOvSv pflt) { return (pflt->fftlen - pflt->buflen); } + +/* where samples should be taken from after filtering */ +#ifdef LHS +COMPLEX * +FiltOvSv_storepoint(FiltOvSv plft) { return ((pflt->zovec) + buflen); } +#else +COMPLEX * +FiltOvSv_storepoint(FiltOvSv pflt) { return ((pflt->zovec)); } +#endif + +/* alternating parity fetch/store */ + +COMPLEX * +FiltOvSv_fetchpt_par(FiltOvSv pflt, int parity) { + if (parity & 01) + return pflt->zovec + pflt->buflen; + else + return pflt->zovec; +} + +COMPLEX * +FiltOvSv_storept_par(FiltOvSv pflt, int parity) { + if (parity & 01) + return pflt->zovec; + else + return pflt->zovec + pflt->buflen; +} + +/* how many samples to take */ +/* NB strategy. This is the number of good samples in the + left half of the true buffer. Samples in right half + are circular artifacts and are ignored. */ +int +FiltOvSv_storesize(FiltOvSv pflt) { return (pflt->fftlen - pflt->buflen); } + +/*------------------------------------------------------------*/ +/* create a new overlap/save filter from complex coefficients */ + +FiltOvSv +newFiltOvSv(COMPLEX *coefs, int ncoef, int pbits) { + int buflen, fftlen; + FiltOvSv p; + fftw_plan pfwd,pinv; + COMPLEX *zrvec, *zfvec, *zivec, *zovec; + p = (FiltOvSv) safealloc(1, sizeof(filt_ov_sv), "new overlap/save filter"); + buflen = nblock2(ncoef-1), fftlen = 2 * buflen; + zrvec = newvec_COMPLEX(fftlen, "raw signal vec in newFiltOvSv"); + zfvec = newvec_COMPLEX(fftlen, "filter z vec in newFiltOvSv"); + zivec = newvec_COMPLEX(fftlen, "signal in z vec in newFiltOvSv"); + zovec = newvec_COMPLEX(fftlen, "signal out z vec in newFiltOvSv"); + + /* prepare frequency response from filter coefs */ + { + int i; + COMPLEX *zcvec; + fftw_plan ptmp; + + zcvec = newvec_COMPLEX(fftlen, "temp filter z vec in newFiltOvSv"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, pbits); + +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = coefs[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef - 1 + i] = coefs[i]; +#endif + + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + } + + /* prepare transforms for signal */ + pfwd = fftw_create_plan(fftlen, FFTW_FORWARD, pbits); + pinv = fftw_create_plan(fftlen, FFTW_BACKWARD, pbits); + /* stuff values */ + p->buflen = buflen; + p->fftlen = fftlen; + p->zfvec = zfvec; + p->zivec = zivec; + p->zovec = zovec; + p->zrvec = zrvec; + p->pfwd = pfwd; + p->pinv = pinv; + p->scale = 1.0 / fftlen; + + return p; +} + +/* deep-six the filter */ +void +delFiltOvSv(FiltOvSv p) { + if (p) { + delvec_COMPLEX(p->zfvec); + delvec_COMPLEX(p->zivec); + delvec_COMPLEX(p->zovec); + delvec_COMPLEX(p->zrvec); + fftw_destroy_plan(p->pfwd); + fftw_destroy_plan(p->pinv); + safefree((char *) p); + } +} + +/*------------------------------------------------------------*/ + + diff --git a/jDttSP/ovsv.h b/jDttSP/ovsv.h new file mode 100644 index 0000000..f4baa16 --- /dev/null +++ b/jDttSP/ovsv.h @@ -0,0 +1,74 @@ +/* ovsv.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _ovsv_h +#define _ovsv_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef +struct _filt_ov_sav { + int buflen, fftlen; + COMPLEX *zfvec, *zivec, *zovec, *zrvec; + fftw_plan pfwd, pinv; + REAL scale; +} filt_ov_sv, *FiltOvSv; + +extern FiltOvSv newFiltOvSv(COMPLEX *coefs, + int ncoef, + int pbits); +extern void delFiltOvSv(FiltOvSv p); + +extern COMPLEX *FiltOvSv_initpoint(FiltOvSv pflt); +extern int FiltOvSv_initsize(FiltOvSv pflt); + +extern COMPLEX *FiltOvSv_fetchpoint(FiltOvSv pflt); +extern int FiltOvSv_fetchsize(FiltOvSv pflt); + +extern COMPLEX *FiltOvSv_storepoint(FiltOvSv pflt); +extern int FiltOvSv_storesize(FiltOvSv pflt); + +extern COMPLEX *FiltOvSv_fetchpt_par(FiltOvSv pflt, int parity); +extern COMPLEX *FiltOvSv_storept_par(FiltOvSv pflt, int parity); + +extern void filter_OvSv(FiltOvSv pflt); +extern void filter_OvSv_par(FiltOvSv pflt); +#endif diff --git a/jDttSP/ringb.c b/jDttSP/ringb.c new file mode 100644 index 0000000..1e6c31e --- /dev/null +++ b/jDttSP/ringb.c @@ -0,0 +1,155 @@ +/* + Memory-mapped ringbuffer + Derived from jack/ringbuffer.h + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Original + Copyright (C) 2000 Paul Davis + Copyright (C) 2003 Rohan Drape + + Derived + Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY +*/ + +#include +#include +#include + +ringb_t * +ringb_create(char *usemem, size_t sz2) { + ringb_t *rb = (ringb_t *) usemem; + rb->buf = usemem + sizeof(ringb_t); + rb->size = sz2; // power-of-2-sized + rb->mask = rb->size - 1; + rb->wptr = rb->rptr = 0; + return rb; +} + +void +ringb_reset(ringb_t *rb) { + // NB not thread-safe + rb->rptr = 0; + rb->wptr = 0; +} + +size_t +ringb_read_space(const ringb_t *rb) { + size_t w = rb->wptr, r = rb->rptr; + if (w > r) return w - r; + else return (w - r + rb->size) & rb->mask; +} + +size_t +ringb_write_space(const ringb_t *rb) { + size_t w = rb->wptr, r = rb->rptr; + if (w > r) return ((r - w + rb->size) & rb->mask) - 1; + else if (w < r) return r - w - 1; + else return rb->size - 1; +} + +size_t +ringb_read(ringb_t *rb, char *dest, size_t cnt) { + size_t free_cnt, cnt2, to_read, n1, n2; + if ((free_cnt = ringb_read_space(rb)) == 0) return 0; + to_read = cnt > free_cnt ? free_cnt : cnt; + if ((cnt2 = rb->rptr + to_read) > rb->size) + n1 = rb->size - rb->rptr, n2 = cnt2 & rb->mask; + else + n1 = to_read, n2 = 0; + memcpy(dest, &(rb->buf[rb->rptr]), n1); + rb->rptr = (rb->rptr + n1) & rb->mask; + if (n2) { + memcpy(dest + n1, &(rb->buf[rb->rptr]), n2); + rb->rptr = (rb->rptr + n2) & rb->mask; + } + return to_read; +} + +size_t +ringb_peek(ringb_t *rb, char *dest, size_t cnt) { + size_t free_cnt, cnt2, to_read, n1, n2, tmp_rptr; + tmp_rptr = rb->rptr; + if ((free_cnt = ringb_read_space(rb)) == 0) return 0; + to_read = cnt > free_cnt ? free_cnt : cnt; + if ((cnt2 = tmp_rptr + to_read) > rb->size) + n1 = rb->size - tmp_rptr, n2 = cnt2 & rb->mask; + else + n1 = to_read, n2 = 0; + memcpy(dest, &(rb->buf[tmp_rptr]), n1); + tmp_rptr = (tmp_rptr + n1) & rb->mask; + if (n2) + memcpy(dest + n1, &(rb->buf[tmp_rptr]), n2); + return to_read; +} + +size_t +ringb_write(ringb_t *rb, const char *src, size_t cnt) { + size_t free_cnt, cnt2, to_write, n1, n2; + if ((free_cnt = ringb_write_space(rb)) == 0) return 0; + to_write = cnt > free_cnt ? free_cnt : cnt; + if ((cnt2 = rb->wptr + to_write) > rb->size) + n1 = rb->size - rb->wptr, n2 = cnt2 & rb->mask; + else + n1 = to_write, n2 = 0; + memcpy(&(rb->buf[rb->wptr]), src, n1); + rb->wptr = (rb->wptr + n1) & rb->mask; + if (n2) { + memcpy(&(rb->buf[rb->wptr]), src + n1, n2); + rb->wptr = (rb->wptr + n2) & rb->mask; + } + return to_write; +} + +void +ringb_read_advance(ringb_t *rb, size_t cnt) { + rb->rptr = (rb->rptr + cnt) & rb->mask; +} + +void +ringb_write_advance(ringb_t *rb, size_t cnt) { + rb->wptr = (rb->wptr + cnt) & rb->mask; +} + +void +ringb_get_read_vector(const ringb_t *rb, ringb_data_t *vec) { + size_t free_cnt, cnt2, + w = rb->wptr, r = rb->rptr; + if (w > r) free_cnt = w - r; + else free_cnt = (w - r + rb->size) & rb->mask; + if ((cnt2 = r + free_cnt) > rb->size) { + vec[0].buf = &(rb->buf[r]), vec[0].len = rb->size - r; + vec[1].buf = rb->buf, vec[1].len = cnt2 & rb->mask; + } else { + vec[0].buf = &(rb->buf[r]), vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +void +ringb_get_write_vector(const ringb_t *rb, ringb_data_t *vec) { + size_t free_cnt, cnt2, + w = rb->wptr, r = rb->rptr; + if (w > r) free_cnt = ((r - w + rb->size) & rb->mask) - 1; + else if (w < r) free_cnt = r - w - 1; + else free_cnt = rb->size - 1; + if ((cnt2 = w + free_cnt) > rb->size) { + vec[0].buf = &(rb->buf[w]), vec[0].len = rb->size - w; + vec[1].buf = rb->buf, vec[1].len = cnt2 & rb->mask; + } else { + vec[0].buf = &(rb->buf[w]), vec[0].len = free_cnt; + vec[1].len = 0; + } +} diff --git a/jDttSP/ringb.h b/jDttSP/ringb.h new file mode 100644 index 0000000..c92fd3c --- /dev/null +++ b/jDttSP/ringb.h @@ -0,0 +1,172 @@ +/* + Memory-mapped ringbuffer + Derived from jack/ringbuffer.h + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Original + Copyright (C) 2000 Paul Davis + Copyright (C) 2003 Rohan Drape + + Derived + Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY +*/ + +#ifndef _ringb_h +#define _ringb_h + +#include + +typedef struct { + char *buf; + size_t len; +} ringb_data_t; + +typedef struct { + char *buf; + size_t wptr, + rptr, + size, + mask; +} ringb_t; + +/* Sets up a ringbuffer data structure of a specified size + * in pre-allocated memory. + * sz is requested ringbuffer size in bytes, + * MUST be a power of 2. + * pre-allocated memory must be large enough to + * accommodate (ringb header + stipulated memory size). + * return a pointer to a new ringb_t, if successful, + * 0 otherwise. */ + +extern ringb_t *ringb_create(char *usemem, size_t sz2); + +/* + * There is no corresponding 'free' function + * since memory is all pre-allocated + */ + +//void ringb_free(ringb_t *rb); + +/* Fill a data structure with a description of the current readable + * data held in the ringbuffer. This description is returned in a two + * element array of ringb_data_t. Two elements are needed + * because the data to be read may be split across the end of the + * ringbuffer. + * + * The first element will always contain a valid len field, which + * may be zero or greater. If the len field is non-zero, then data + * can be read in a contiguous fashion using the address given in the + * corresponding @a buf field. + * + * If the second element has a non-zero len field, then a second + * contiguous stretch of data can be read from the address given in + * its corresponding buf field. + * + * rb a pointer to the ringbuffer structure. + * vec a pointer to a 2 element array of ringb_data_t. */ + +extern void ringb_get_read_vector(const ringb_t *rb, ringb_data_t *vec); + +/* Fill a data structure with a description of the current writable + * space in the ringbuffer. The description is returned in a two + * element array of ringb_data_t. Two elements are needed + * because the space available for writing may be split across the end + * of the ringbuffer. + * The first element will always contain a valid len field, which + * may be zero or greater. If the @a len field is non-zero, then data + * can be written in a contiguous fashion using the address given in + * the corresponding buf field. + * If the second element has a non-zero len field, then a second + * contiguous stretch of data can be written to the address given in + * the corresponding buf field. + * rb a pointer to the ringbuffer structure. + * vec a pointer to a 2 element array of ringb_data_t. */ + +extern void ringb_get_write_vector(const ringb_t *rb, ringb_data_t *vec); + +/* + * Read data from the ringbuffer. + * rb a pointer to the ringbuffer structure. + * dest a pointer to a buffer where data read from the + * ringbuffer will go. + * cnt the number of bytes to read. + * + * return the number of bytes read, which may range from 0 to cnt. */ + +extern size_t ringb_read(ringb_t *rb, char *dest, size_t cnt); + +/* Read data from the ringbuffer. Opposed to ringb_read() + * this function does not move the read pointer. Thus it's + * a convenient way to inspect data in the ringbuffer in a + * continous fashion. The price is that the data is copied + * into a user provided buffer. For "raw" non-copy inspection + * of the data in the ringbuffer use ringb_get_read_vector(). + * rb a pointer to the ringbuffer structure. + * dest a pointer to a buffer where data read from the + * ringbuffer will go. + * cnt the number of bytes to read. + * return the number of bytes read, which may range from 0 to cnt. + */ + +extern size_t ringb_peek(ringb_t *rb, char *dest, size_t cnt); + +/* Advance the read pointer. + * After data have been read from the ringbuffer using the pointers + * returned by ringb_get_read_vector(), use this function to + * advance the buffer pointers, making that space available for future + * write operations. + * + * rb a pointer to the ringbuffer structure. + * cnt the number of bytes read. */ + +extern void ringb_read_advance(ringb_t *rb, size_t cnt); + +/* Return the number of bytes available for reading. + * rb a pointer to the ringbuffer structure. + * return the number of bytes available to read. */ + +extern size_t ringb_read_space(const ringb_t *rb); + +/* Reset the read and write pointers, making an empty buffer. + * This is not thread safe. */ + +extern void ringb_reset(ringb_t *rb); + +/* Write data into the ringbuffer. + * rb a pointer to the ringbuffer structure. + * src a pointer to the data to be written to the ringbuffer. + * cnt the number of bytes to write. + * return the number of bytes write, which may range from 0 to cnt */ + +extern size_t ringb_write(ringb_t *rb, const char *src, size_t cnt); + +/* Advance the write pointer. + * After data have been written the ringbuffer using the pointers + * returned by ringb_get_write_vector(), use this function + * to advance the buffer pointer, making the data available for future + * read operations. + * rb a pointer to the ringbuffer structure. + * cnt the number of bytes written. */ + +extern void ringb_write_advance(ringb_t *rb, size_t cnt); + +/* Return the number of bytes available for writing. + * rb a pointer to the ringbuffer structure. + * return the amount of free space (in bytes) available for writing. */ + +extern size_t ringb_write_space(const ringb_t *rb); + +#endif diff --git a/jDttSP/sdr.c b/jDttSP/sdr.c new file mode 100644 index 0000000..f04d641 --- /dev/null +++ b/jDttSP/sdr.c @@ -0,0 +1,676 @@ +/* sdr.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//======================================================================== +/* initialization and termination */ + +/* global and general info, + not specifically attached to + tx, rx, or scheduling */ + +PRIVATE void +setup_all(void) { + + uni.samplerate = loc.def.rate; + uni.buflen = loc.def.size; + uni.mode.sdr = loc.def.mode; + uni.mode.trx = RX; + + if (uni.meter.flag) { + uni.meter.chan.path = loc.path.meter; + uni.meter.chan.size = loc.mult.ring * sizeof(REAL); + uni.meter.val = -200.0; + uni.meter.chan.c = openChan(uni.meter.chan.path, uni.meter.chan.size); + } + + uni.wisdom.path = loc.path.wisdom; + uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE; + { + FILE *f = fopen(uni.wisdom.path, "r"); + if (f) { +#define WBUFLEN 2048 +#define WSTRLEN 64 + char *line = alloca(WBUFLEN); + fgets(line, WBUFLEN, f); + if ((strlen(line) > WSTRLEN) && + (fftw_import_wisdom_from_string(line) != FFTW_FAILURE)) + uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_MEASURE | FFTW_USE_WISDOM; +#undef WSTRLEN +#undef WBUFLEN + fclose(f); + } + } + + uni.tick = 0; +} + +/* purely rx */ + +PRIVATE void +setup_rx(void) { + + /* conditioning */ + rx.iqfix = newCorrectIQ(0.0, 1.0); + rx.filt.coef = newFIR_Bandpass_COMPLEX(-4800.0, + 4800.0, + uni.samplerate, + uni.buflen + 1); + rx.filt.ovsv = newFiltOvSv(FIRcoef(rx.filt.coef), + FIRsize(rx.filt.coef), + uni.wisdom.bits); + normalize_vec_COMPLEX(rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen); + + // hack for EQ + rx.filt.save = newvec_COMPLEX(rx.filt.ovsv->fftlen, "RX filter cache"); + memcpy((char *) rx.filt.save, + (char *) rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + /* buffers */ + /* note we overload the internal filter buffers + we just created */ + rx.buf.i = newCXB(FiltOvSv_fetchsize(rx.filt.ovsv), + FiltOvSv_fetchpoint(rx.filt.ovsv), + "init rx.buf.i"); + rx.buf.o = newCXB(FiltOvSv_storesize(rx.filt.ovsv), + FiltOvSv_storepoint(rx.filt.ovsv), + "init rx.buf.o"); + + /* conversion */ + rx.osc.freq = -11025.0; + rx.osc.phase = 0.0; + rx.osc.gen = newOSC(uni.buflen, + ComplexTone, + rx.osc.freq, + rx.osc.phase, + uni.samplerate, + "SDR RX Oscillator"); + + rx.agc.gen = newDigitalAgc(agcMED, // Mode + 7, // Hang + 7, // Size + 48, // Ramp + 3, // Over + 3, // Rcov + CXBsize(rx.buf.o), // BufSize + 100.0, // MaxGain + 0.707, // Limit + 1.0, // CurGain + CXBbase(rx.buf.o)); + rx.agc.flag = TRUE; + + /* demods */ + rx.am.gen = newAMD(48000.0, // REAL samprate + 0.0, // REAL f_initial + -500.0, // REAL f_lobound, + 500.0, // REAL f_hibound, + 400.0, // REAL f_bandwid, + CXBsize(rx.buf.o), // int size, + CXBbase(rx.buf.o), // COMPLEX *ivec, + CXBbase(rx.buf.o), // COMPLEX *ovec, + AMdet, // AM Mode AMdet == rectifier, + // SAMdet == synchronous detector + "AM detector blew"); // char *tag + rx.fm.gen = newFMD(48000, // REAL samprate + 0.0, // REAL f_initial + -6000.0, // REAL f_lobound + 6000.0, // REAL f_hibound + 10000.0, // REAL f_bandwid + CXBsize(rx.buf.o), // int size + CXBbase(rx.buf.o), // COMPLEX *ivec + CXBbase(rx.buf.o), // COMPLEX *ovec + "New FM Demod structure"); // char *error message; + + /* noise reduction */ + rx.anf.gen = new_lmsr(rx.buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_INTERFERENCE); + rx.anf.flag = FALSE; + rx.anr.gen = new_lmsr(rx.buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_NOISE); + rx.anr.flag = FALSE; + + rx.nb.thresh = 3.3; + rx.nb.gen = new_noiseblanker(rx.buf.i, rx.nb.thresh); + rx.nb.flag = FALSE; + + rx.nb_sdrom.thresh = 2.5; + rx.nb_sdrom.gen = new_noiseblanker(rx.buf.i, rx.nb_sdrom.thresh); + rx.nb_sdrom.flag = FALSE; + + rx.spot.gen = newSpotToneGen(-12.0, // gain + 700.0, // freq + 5.0, // ms rise + 5.0, // ms fall + uni.buflen, + uni.samplerate); + + rx.scl.pre.val = 1.0; + rx.scl.pre.flag = FALSE; + rx.scl.post.val = 1.0; + rx.scl.post.flag = FALSE; + + memset((char *) &rx.squelch, 0, sizeof(rx.squelch)); + rx.squelch.thresh = -30.0; + rx.squelch.power = 0.0; + rx.squelch.flag = rx.squelch.running = rx.squelch.set = FALSE; + rx.squelch.num = (int) (0.0395 * uni.samplerate + 0.5); + + rx.mode = uni.mode.sdr; + rx.bin.flag = FALSE; + + rx.tick = 0; +} + +/* purely tx */ + +PRIVATE void +setup_tx(void) { + + /* conditioning */ + tx.iqfix = newCorrectIQ(0.0, 1.0); + tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0, + 3000.0, + uni.samplerate, + uni.buflen + 1); + tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef), + FIRsize(tx.filt.coef), + uni.wisdom.bits); + normalize_vec_COMPLEX(tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen); + + // hack for EQ + tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache"); + memcpy((char *) tx.filt.save, + (char *) tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + /* buffers */ + tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv), + FiltOvSv_fetchpoint(tx.filt.ovsv), + "init tx.buf.i"); + tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv), + FiltOvSv_storepoint(tx.filt.ovsv), + "init tx.buf.o"); + + /* conversion */ + tx.osc.freq = 0.0; + tx.osc.phase = 0.0; + tx.osc.gen = newOSC(uni.buflen, + ComplexTone, + tx.osc.freq, + tx.osc.phase, + uni.samplerate, + "SDR TX Oscillator"); + + tx.agc.gen = newDigitalAgc(agcFAST, // Mode + 3, // Hang + 3, // Size + 3, // Over + 3, // Rcov + 48, // Ramp + CXBsize(tx.buf.o), // BufSize + 1.0, // MaxGain + 0.900, // Limit + 1.0, // CurGain + CXBbase(tx.buf.o)); + tx.agc.flag = TRUE; + + tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i)); + tx.spr.flag = FALSE; + + tx.scl.dc = cxzero; + tx.scl.pre.val = 1.0; + tx.scl.pre.flag = FALSE; + tx.scl.post.val = 1.0; + tx.scl.post.flag = FALSE; + + tx.mode = uni.mode.sdr; + + tx.tick = 0; + /* not much else to do for TX */ +} + +/* how the outside world sees it */ + +void +setup_workspace(void) { + setup_all(), setup_rx(), setup_tx(); +} + +void +destroy_workspace(void) { + + /* TX */ + delSpeechProc(tx.spr.gen); + delDigitalAgc(tx.agc.gen); + delOSC(tx.osc.gen); + delvec_COMPLEX(tx.filt.save); + delFiltOvSv(tx.filt.ovsv); + delFIR_Bandpass_COMPLEX(tx.filt.coef); + delCorrectIQ(tx.iqfix); + delCXB(tx.buf.o); + delCXB(tx.buf.i); + + /* RX */ + delSpotToneGen(rx.spot.gen); + delDigitalAgc(rx.agc.gen); + del_nb(rx.nb_sdrom.gen); + del_nb(rx.nb.gen); + del_lmsr(rx.anf.gen); + del_lmsr(rx.anr.gen); + delAMD(rx.am.gen); + delFMD(rx.fm.gen); + delOSC(rx.osc.gen); + delvec_COMPLEX(rx.filt.save); + delFiltOvSv(rx.filt.ovsv); + delFIR_Bandpass_COMPLEX(rx.filt.coef); + delCorrectIQ(rx.iqfix); + delCXB(rx.buf.o); + delCXB(rx.buf.i); + + /* all */ + if (uni.meter.flag) + closeChan(uni.meter.chan.c); +} + +////////////////////////////////////////////////////////////////////////// +// execution +////////////////////////////////////////////////////////////////////////// + +//======================================================================== +/* all */ + +/* tap off S-meter from some buf */ + +PRIVATE void +do_meter(COMPLEX *vec, int len) { + int i; + + uni.meter.val = 0; + + switch (uni.meter.type) { + case AVG_SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.val += Csqrmag(vec[i]); + uni.meter.val = + uni.meter.avgval = 0.9 * uni.meter.avgval + log10(uni.meter.val + 1e-20); + break; + case SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.val += Csqrmag(vec[i]); + uni.meter.avgval = uni.meter.val = 10.0 * log10(uni.meter.val + 1e-20); + break; + case ADC_REAL: + for(i = 0; i < len; i++) + uni.meter.val = max(fabs(vec[i].re), uni.meter.val); + uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10); + break; + case ADC_IMAG: + for(i = 0; i < len; i++) + uni.meter.val = max(fabs(vec[i].im), uni.meter.val); + uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10); + break; + default: + break; + } + + putChan_nowait(uni.meter.chan.c, + (char *) &uni.meter.val, + sizeof(uni.meter.val)); +} + +//======================================================================== +/* RX processing */ + +PRIVATE BOOLEAN +should_do_rx_squelch(void) { + if (rx.squelch.flag) { + int i, n = CXBhave(rx.buf.o); + REAL tst; + rx.squelch.power = 0.0; + for (i = 0; i < n; i++) + rx.squelch.power += Csqrmag(CXBdata(rx.buf.o, i)); + tst = (10.0 * log10(rx.squelch.power) + + rx.squelch.offset.meter + + rx.squelch.offset.att + + rx.squelch.offset.gain); + return rx.squelch.thresh > tst; + } else + return rx.squelch.set = FALSE; +} + +// apply squelch +// slew into silence first time + +PRIVATE void +do_squelch(void) { + rx.squelch.set = TRUE; + if (!rx.squelch.running) { + int i, m = rx.squelch.num, n = CXBhave(rx.buf.o) - m; + for (i = 0; i < m; i++) + CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), 1.0 - (REAL) i / m); + memset((void *) (CXBbase(rx.buf.o) + m), 0, n * sizeof(COMPLEX)); + rx.squelch.running = TRUE; + } else + memset((void *) CXBbase(rx.buf.o), 0, CXBhave(rx.buf.o) * sizeof(COMPLEX)); +} + +// lift squelch +// slew out from silence to full scale + +PRIVATE void +no_squelch(void) { + if (rx.squelch.running) { + int i, m = rx.squelch.num; + for (i = 0; i < m; i++) + CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), (REAL) i / m); + rx.squelch.running = FALSE; + } +} + +/* pre-condition for (nearly) all RX modes */ + +PRIVATE void +do_rx_pre(void) { + int i, n = min(CXBhave(rx.buf.i), uni.buflen); + + // + // do shrinkage here + // + + if (rx.scl.pre.flag) + for (i = 0; i < n; i++) + CXBdata(rx.buf.i, i) = Cscl(CXBdata(rx.buf.i, i), + rx.scl.pre.val); + + if (rx.nb.flag) noiseblanker(rx.nb.gen); + if (rx.nb_sdrom.flag) SDROMnoiseblanker(rx.nb_sdrom.gen); + + correctIQ(rx.buf.i, rx.iqfix); + + /* 2nd if conversion happens here */ + if (rx.osc.gen->Frequency != 0.0) { + ComplexOSC(rx.osc.gen); + for (i = 0; i < n; i++) + CXBdata(rx.buf.i, i) = Cmul(CXBdata(rx.buf.i, i), + OSCCdata(rx.osc.gen, i)); + } + + /* filtering, metering, & AGC */ + if (rx.mode != SPEC) { + filter_OvSv(rx.filt.ovsv); + CXBhave(rx.buf.o) = CXBhave(rx.buf.i); + if (uni.meter.flag) do_meter(CXBbase(rx.buf.o), uni.buflen); + if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick); + } else if (uni.meter.flag) + do_meter(CXBbase(rx.buf.o), uni.buflen); +} + +PRIVATE void +do_rx_post(void) { + int i, n = CXBhave(rx.buf.o);; + + if (!rx.squelch.set) { + no_squelch(); + // spotting tone + if (rx.spot.flag) { + // remember whether it's turned itself off during this pass + rx.spot.flag = SpotTone(rx.spot.gen); + for (i = 0; i < n; i++) + CXBdata(rx.buf.o, i) = Cadd(CXBdata(rx.buf.o, i), + CXBdata(rx.spot.gen->buf, i)); + } + } + + // + // mix in sidetone here + // + + if (rx.scl.post.flag) + for (i = 0; i < n; i++) + CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), + rx.scl.post.val); + + // not binaural? collapse + if (!rx.bin.flag) + for (i = 0; i < n; i++) + CXBimag(rx.buf.o, i) = CXBreal(rx.buf.o, i); +} + +/* demod processing */ + +PRIVATE void +do_rx_SBCW(void) { + if (rx.anr.flag) lmsr_adapt(rx.anr.gen); + if (rx.anf.flag) lmsr_adapt(rx.anf.gen); +} + +PRIVATE void +do_rx_AM(void) { AMDemod(rx.am.gen); } + +PRIVATE void +do_rx_FM(void) { FMDemod(rx.fm.gen); } + +PRIVATE void +do_rx_DRM(void) {} + +PRIVATE void +do_rx_SPEC(void) { + memcpy(CXBbase(rx.buf.o), + CXBbase(rx.buf.i), + sizeof(COMPLEX) * CXBhave(rx.buf.i)); + if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick); +} + +PRIVATE void +do_rx_NIL(void) { + int i, n = min(CXBhave(rx.buf.i), uni.buflen); + for (i = 0; i < n; i++) CXBdata(rx.buf.o, i) = cxzero; +} + +/* overall dispatch for RX processing */ + +PRIVATE void +do_rx(void) { + do_rx_pre(); + switch (rx.mode) { + case USB: + case LSB: + case CWU: + case CWL: + case DSB: do_rx_SBCW(); break; + case AM: + case SAM: do_rx_AM(); break; + case FMN: do_rx_FM(); break; + case DRM: do_rx_DRM(); break; + case SPEC: + default: do_rx_SPEC(); break; + } + do_rx_post(); +} + +//============================================================== +/* TX processing */ + +/* pre-condition for (nearly) all TX modes */ + +PRIVATE void +do_tx_pre(void) { + if (tx.scl.pre.flag) { + int i, n = CXBhave(tx.buf.i); + for (i = 0; i < n; i++) + CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0); + } + + // + // mix in CW tone here + // + + correctIQ(tx.buf.i, tx.iqfix); + + if (tx.spr.flag) SpeechProcessor(tx.spr.gen); + filter_OvSv(tx.filt.ovsv); +} + +PRIVATE void +do_tx_post(void) { + CXBhave(tx.buf.o) = CXBhave(tx.buf.i); + + if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick); + if (tx.scl.post.flag) { + int i, n = CXBhave(tx.buf.o); + for (i = 0; i < n; i++) + CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val); + } + if (uni.meter.flag) do_meter(CXBbase(tx.buf.o), uni.buflen); + if (tx.osc.gen->Frequency != 0.0) { + int i; + ComplexOSC(tx.osc.gen); + for (i = 0; i < CXBhave(tx.buf.o); i++) + CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i)); + } +} + +/* modulator processing */ + +PRIVATE void +do_tx_SBCW(void) { + int i, n = min(CXBhave(tx.buf.i), uni.buflen); + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99), + Cscl(CXBdata(tx.buf.o,i), -0.01)); + CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o,i), tx.scl.dc); + } +} + +PRIVATE void +do_tx_AM(void) { + int i, n = min(CXBhave(tx.buf.i), uni.buflen); + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), + Cscl(CXBdata(tx.buf.o,i), -0.001)); + CXBreal(tx.buf.o, i) = + 0.49995 + 0.49995 * (CXBreal(tx.buf.o,i) - tx.scl.dc.re); + CXBimag(tx.buf.o, i) = 0.0; + } +} + +PRIVATE void +do_tx_FM(void) { + int i, n = min(CXBhave(tx.buf.i), uni.buflen); + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc,0.999), + Cscl(CXBdata(tx.buf.o,i), 0.001)); + tx.osc.phase += (CXBreal(tx.buf.o, i)- tx.scl.dc.re) * CvtMod2Freq; + if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI; + if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI; + CXBdata(tx.buf.o, i) = + Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999); + } +} + +PRIVATE void +do_tx_NIL(void) { + int i, n = min(CXBhave(tx.buf.i), uni.buflen); + for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero; +} + +/* general TX processing dispatch */ + +PRIVATE void +do_tx(void) { + do_tx_pre(); + switch (tx.mode) { + case USB: + case LSB: + case CWU: + case CWL: + case DSB: do_tx_SBCW(); break; + case AM: + case SAM: do_tx_AM(); break; + case FMN: do_tx_FM(); break; + case DRM: + case SPEC: + default: do_tx_NIL(); break; + } + do_tx_post(); +} + +//======================================================================== +/* overall buffer processing; + come here when there are buffers to work on */ + +void +process_samples(float *bufl, float *bufr, int n) { + int i; + + switch (uni.mode.trx) { + + case RX: + for (i = 0; i < n; i++) + CXBimag(rx.buf.i, i) = bufl[i], CXBreal(rx.buf.i, i) = bufr[i]; + CXBhave(rx.buf.i) = n; + + do_rx(), rx.tick++; + + for (i = 0; i < n; i++) + bufl[i] = (float)CXBimag(rx.buf.o, i), bufr[i] = (float)CXBreal(rx.buf.o, i); + CXBhave(rx.buf.o) = n; + break; + + case TX: + for (i = 0; i < n; i++) + CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i]; + CXBhave(tx.buf.i) = n; + + do_tx(), tx.tick++; + + for (i = 0; i < n; i++) + bufl[i] = (float)CXBimag(tx.buf.o, i), bufr[i] = (float)CXBreal(tx.buf.o, i); + CXBhave(rx.buf.o) = n; + break; + } + + uni.tick++; +} diff --git a/jDttSP/sdrexport.c b/jDttSP/sdrexport.c new file mode 100644 index 0000000..1830bd5 --- /dev/null +++ b/jDttSP/sdrexport.c @@ -0,0 +1,39 @@ +/* sdrexport.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +struct _uni uni; +struct _rx rx; +struct _tx tx; +struct _top top; diff --git a/jDttSP/sdrexport.h b/jDttSP/sdrexport.h new file mode 100644 index 0000000..b561bf7 --- /dev/null +++ b/jDttSP/sdrexport.h @@ -0,0 +1,284 @@ +/* sdrexport.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _sdrexport_h +#define _sdrexport_h + +#include + +//------------------------------------------------------------------------ +/* modulation types, modes */ +typedef enum _sdrmode { + LSB, // 0 + USB, // 1 + DSB, // 2 + CWL, // 3 + CWU, // 4 + FMN, // 5 + AM, // 6 + PSK, // 7 + SPEC, // 8 + RTTY, // 9 + SAM, // 10 + DRM // 11 +} SDRMODE; + +typedef enum _trxmode { RX, TX } TRXMODE; + +//======================================================================== +/* RX/TX both */ +//------------------------------------------------------------------------ +extern struct _uni { + REAL samplerate; + int buflen; + + struct { + SDRMODE sdr; + TRXMODE trx; + } mode; + + struct { + BOOLEAN flag; + struct { + char *path; + size_t size; + Chan c; + } chan; + REAL val, avgval; + METERTYPE type; + } meter; + + struct { + BOOLEAN flag; + struct { + char *path; + size_t size; + Chan c; + } chan; + splitfld splt; + } update; + + struct { + char *path; + int bits; + } wisdom; + + long tick; + +} uni; + +//------------------------------------------------------------------------ +/* RX */ +//------------------------------------------------------------------------ + +extern struct _rx { + struct { + CXB i, o; + } buf; + IQ iqfix; + struct { + REAL freq, phase; + OSC gen; + } osc; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + COMPLEX *save; + } filt; + struct { + REAL thresh; + NB gen; + BOOLEAN flag; + } nb; + struct { + REAL thresh; + NB gen; + BOOLEAN flag; + } nb_sdrom; + struct { + LMSR gen; + BOOLEAN flag; + } anr, anf; + struct { + DIGITALAGC gen; + BOOLEAN flag; + } agc; + struct { AMD gen; } am; + struct { FMD gen; } fm; + struct { + BOOLEAN flag; + SpotToneGen gen; + } spot; + struct { + struct { + REAL val; + BOOLEAN flag; + } pre, post; + } scl; + struct { + REAL thresh, power; + struct { + REAL meter, att, gain; + } offset; + BOOLEAN flag, running, set; + int num; + } squelch; + SDRMODE mode; + struct { BOOLEAN flag; } bin; + long tick; +} rx; + +//------------------------------------------------------------------------ +/* TX */ +//------------------------------------------------------------------------ +extern struct _tx { + struct { + CXB i, o; + } buf; + IQ iqfix; + struct { + REAL freq, phase; + OSC gen; + } osc; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + COMPLEX *save; + } filt; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + CXB in, out; + } fm; + struct { + DIGITALAGC gen; + BOOLEAN flag; + } agc; + struct { + SpeechProc gen; + BOOLEAN flag; + } spr; + struct { + COMPLEX dc; + struct { + REAL val; + BOOLEAN flag; + } pre, post; + } scl; + SDRMODE mode; + long tick; +} tx; + +//------------------------------------------------------------------------ + +typedef enum _runmode { + RUN_MUTE, RUN_PASS, RUN_PLAY, RUN_SWCH +} RUNMODE; + +extern struct _top { + pid_t pid; + uid_t uid; + + struct timeval start_tv; + + BOOLEAN running, verbose; + RUNMODE state; + + // audio io + struct { + struct { + float *l, *r; + } buf; + struct { + unsigned int frames, bytes; + } size; + } hold; + struct { + char *path; + int fd; + FILE *fp; + char buff[4096]; + } parm; + + struct { + char name[256]; + jack_client_t *client; + struct { + struct { + jack_port_t *l, *r; + } i, o; + } port; + struct { + struct { + jack_ringbuffer_t *l, *r; + } i, o; + } ring; + jack_nframes_t size; + struct { + int cb; + struct { + int i, o; + } rb; + int xr; + } blow; + } jack; + + // update io + // multiprocessing & synchronization + struct { + struct { + pthread_t id; + } trx, upd, mon; + } thrd; + struct { + struct { + sem_t sem; + } buf, upd, mon; + } sync; + + // TRX switching + struct { + struct { + int want, have; + } bfct; + struct { + TRXMODE next; + } trx; + struct { + RUNMODE last; + } run; + int fade, tail; + } swch; +} top; + +#endif diff --git a/jDttSP/setup-ipc b/jDttSP/setup-ipc new file mode 100755 index 0000000..3a58d31 --- /dev/null +++ b/jDttSP/setup-ipc @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ ! -d ./IPC ]; then mkdir ./IPC; fi + +(cd ./IPC + if [ ! -p SDR-1000-0-commands.fifo ]; then + mkfifo SDR-1000-0-commands.fifo + fi + + # 192 = METERMULT * sizeof(REAL) + if [ ! -f SDR-1000-0-meter.chan ]; then + ../mkchan SDR-1000-0-meter.chan 192 + fi) + +ls -ld IPC +ls -l IPC diff --git a/jDttSP/speechproc.c b/jDttSP/speechproc.c new file mode 100644 index 0000000..505304d --- /dev/null +++ b/jDttSP/speechproc.c @@ -0,0 +1,83 @@ +/* speechproc.c + + This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +SpeechProc +newSpeechProc(REAL K, REAL MaxCompression, COMPLEX *spdat, int size) { + SpeechProc sp = (SpeechProc) safealloc(1, sizeof(speech_proc), "new speech processor"); + sp->CG = newRLB(size, NULL, "CG buffer in Speech Processor"); + sp->K = K; + sp->MaxGain = pow(10.0, MaxCompression * 0.05); + sp->LastCG = 1.0; + sp->SpeechProcessorBuffer = newCXB(size, spdat, "speech processor data"); + sp->size = size; + return sp; +} + +void +delSpeechProc(SpeechProc sp) { + if (sp) { + delCXB(sp->SpeechProcessorBuffer); + safefree((char *) sp); + } +} + +void +SpeechProcessor(SpeechProc sp) { + int i; + REAL r = 0.0, Mag; + for (i = 0; i < sp->size; i++) + r = max(r, Cmag(CXBdata(sp->SpeechProcessorBuffer, i))); + Mag = Cmag(CXBdata(sp->SpeechProcessorBuffer, 0)); + if (Mag > 0.0) { + RLBdata(sp->CG, 0) = sp->LastCG * (1.0 - sp->K) + (sp->K * r / Mag); + if (RLBdata(sp->CG, 0) > sp->MaxGain) + RLBdata(sp->CG, 0) = sp->MaxGain; + } else + RLBdata(sp->CG, 0) = 1.0; + for (i = 1; i < sp->size; i++) { + Mag = Cmag(CXBdata(sp->SpeechProcessorBuffer, i)); + if (Mag > 0.0) { + RLBdata(sp->CG, i) = + RLBdata(sp->CG, i - 1) * (1 - sp->K) + (sp->K * r / Mag); + if (RLBdata(sp->CG, i) > sp->MaxGain) + RLBdata(sp->CG, i) = sp->MaxGain; + } else + RLBdata(sp->CG, i) = 1.0; + } + sp->LastCG = RLBdata(sp->CG, sp->size - 1); + for (i = 0; i < sp->size; i++) + CXBdata(sp->SpeechProcessorBuffer, i) = + Cscl(CXBdata(sp->SpeechProcessorBuffer, i), RLBdata(sp->CG, i)); +} diff --git a/jDttSP/speechproc.h b/jDttSP/speechproc.h new file mode 100644 index 0000000..cd69331 --- /dev/null +++ b/jDttSP/speechproc.h @@ -0,0 +1,54 @@ +/* speechproc.h + + This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _speechproc_h +#define _speechproc_h + +#include +#include +#include +#include +#include + +typedef struct _speechprocessor { + int size; + RLB CG; + CXB SpeechProcessorBuffer; + REAL LastCG, K, MaxGain; +} speech_proc, *SpeechProc; + +extern SpeechProc newSpeechProc(REAL, REAL, COMPLEX *, int); +extern void delSpeechProc(SpeechProc); +extern void SpeechProcessor(SpeechProc); + +#endif diff --git a/jDttSP/splitfields.c b/jDttSP/splitfields.c new file mode 100644 index 0000000..3e5adaf --- /dev/null +++ b/jDttSP/splitfields.c @@ -0,0 +1,73 @@ +/* splitfields.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +static char *_white = " \t\n"; + +SPLIT +newSPLIT(void) { + return (SPLIT) safealloc(1, sizeof(splitfld), "splitfield"); +} + +void +delSPLIT(SPLIT s) { if (s) free((char *) s); } + +char * +F(SPLIT s, int i) { return s->f[i]; } + +char ** +Fptr(SPLIT s, int i) { return &(s->f[i]); } + +int +NF(SPLIT s) { return s->n; } + +int +splitonto(SPLIT s, char *str, char *delim, char **fld, int fmx) { + int i = 0; + char *p = strtok(str, delim); + while (p) { + fld[i] = p; + if (++i >= fmx) break; + p = strtok(0, delim); + } + return i; +} + +int +spliton(SPLIT s, char *str, char *delim) { + return (s->n = splitonto(s, str, delim, s->f, MAXFLD)); +} + +void +split(SPLIT s, char *str) { spliton(s, str, _white); } diff --git a/jDttSP/splitfields.h b/jDttSP/splitfields.h new file mode 100644 index 0000000..dba31f7 --- /dev/null +++ b/jDttSP/splitfields.h @@ -0,0 +1,56 @@ +/* splitfields.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _splitfields_h +#define _splitfields_h + +#include +#include +#include + +#define MAXFLD 16384 + +typedef struct _splitfld { + char *f[MAXFLD]; + int n; +} splitfld, *SPLIT; + +extern char *F(SPLIT, int); +extern char **Fptr(SPLIT, int); +extern int NF(SPLIT); + +extern int splitonto(SPLIT, char *, char *, char **, int); +extern int spliton(SPLIT, char *, char *); +extern void split(SPLIT, char *); + +#endif diff --git a/jDttSP/spottone.c b/jDttSP/spottone.c new file mode 100644 index 0000000..e4b2aa2 --- /dev/null +++ b/jDttSP/spottone.c @@ -0,0 +1,153 @@ +/* spottone.c */ + +#include + +//------------------------------------------------------------------------ +// An ASR envelope on a complex phasor, +// with asynchronous trigger for R stage. +// A/R use sine shaping. +//------------------------------------------------------------------------ + +BOOLEAN +SpotTone(SpotToneGen st) { + int i, n = st->size; + + ComplexOSC(st->osc.gen); + + for (i = 0; i < n; i++) { + + // in an envelope stage? + + if (st->stage == SpotTone_RISE) { + + // still going? + if (st->rise.have++ < st->rise.want) { + st->curr += st->rise.incr; + st->mul = st->scl * sin(st->curr * M_PI / 2.0); + } else { + // no, assert steady-state, force level + st->curr = 1.0; + st->mul = st->scl; + st->stage = SpotTone_STDY; + // won't come back into envelopes + // until FALL asserted from outside + } + + } else if (st->stage == SpotTone_FALL) { + + // still going? + if (st->fall.have++ < st->fall.want) { + st->curr -= st->fall.incr; + st->mul = st->scl * sin(st->curr * M_PI / 2.0); + } else { + // no, assert trailing, force level + st->curr = 0.0; + st->mul = 0.0; + st->stage = SpotTone_HOLD; + // won't come back into envelopes hereafter + } + } + + // apply envelope + // (same base as osc.gen internal buf) + CXBdata(st->buf, i) = Cscl(CXBdata(st->buf, i), st->mul); + } + + // indicate whether it's turned itself off + // sometime during this pass + + return st->stage != SpotTone_HOLD; +} + +//------------------------------------------------------------------------ +// turn spotting on with current settings + +void +SpotToneOn(SpotToneGen st) { + + // gain is in dB + + st->scl = pow(10.0, st->gain / 20.0); + st->curr = st->mul = 0.0; + + // A/R times are in msec + + st->rise.want = (int) (0.5 + st->sr * (st->rise.dur / 1e3)); + st->rise.have = 0; + if (st->rise.want <= 1) + st->rise.incr = 1.0; + else + st->rise.incr = 1.0 / (st->rise.want - 1); + + st->fall.want = (int) (0.5 + st->sr * (st->fall.dur / 1e3)); + st->fall.have = 0; + if (st->fall.want <= 1) + st->fall.incr = 1.0; + else + st->fall.incr = 1.0 / (st->fall.want - 1); + + // freq is in Hz + + OSCfreq(st->osc.gen) = 2.0 * M_PI * st->osc.freq / st->sr; + OSCphase(st->osc.gen) = 0.0; + + st->stage = SpotTone_RISE; +} + +//------------------------------------------------------------------------ +// initiate turn-off + +void +SpotToneOff(SpotToneGen st) { st->stage = SpotTone_FALL; } + +//------------------------------------------------------------------------ + +void +setSpotToneGenVals(SpotToneGen st, + REAL gain, + REAL freq, + REAL rise, + REAL fall) { + st->gain = gain; + st->osc.freq = freq; + st->rise.dur = rise; + st->fall.dur = fall; +} + +SpotToneGen +newSpotToneGen(REAL gain, // dB + REAL freq, + REAL rise, // ms + REAL fall, // ms + int size, + REAL samplerate) { + + SpotToneGen st = (SpotToneGen) safealloc(1, + sizeof(SpotToneGenDesc), + "SpotToneGenDesc"); + + setSpotToneGenVals(st, gain, freq, rise, fall); + st->size = size; + st->sr = samplerate; + + st->osc.gen = newOSC(st->size, + ComplexTone, + st->osc.freq, + 0.0, + st->sr, + "SpotTone osc"); + + // overload oscillator buf + st->buf = newCXB(st->size, OSCCbase(st->osc.gen), "SpotToneGen buf"); + + return st; +} + +void +delSpotToneGen(SpotToneGen st) { + if (st) { + delCXB(st->buf); + delOSC(st->osc.gen); + safefree((char *) st); + } +} diff --git a/jDttSP/spottone.h b/jDttSP/spottone.h new file mode 100644 index 0000000..1b46240 --- /dev/null +++ b/jDttSP/spottone.h @@ -0,0 +1,50 @@ +/* spottone.h */ + +#ifndef _spottone_h +#define _spottone_h + +#include +#include +#include +#include +#include +#include + +#define SpotTone_IDLE (0) +#define SpotTone_WAIT (1) +#define SpotTone_RISE (2) +#define SpotTone_STDY (3) +#define SpotTone_FALL (4) +#define SpotTone_HOLD (5) + +typedef struct _spot_tone_gen { + REAL curr, gain, mul, scl, sr; + struct { + REAL freq; + OSC gen; + } osc; + struct { + REAL dur, incr; + int want, have; + } rise, fall; + int size, stage; + CXB buf; +} SpotToneGenDesc, *SpotToneGen; + +extern SpotToneGen newSpotToneGen(REAL gain, // dB + REAL freq, // Hz + REAL rise, // msec + REAL fall, // msec + int size, // buflen + REAL samplerate); +extern void delSpotToneGen(SpotToneGen gen); +extern void setSpotToneGenVals(SpotToneGen gen, + REAL gain, + REAL freq, + REAL rise, + REAL fall); +extern void SpotToneOn(SpotToneGen gen); +extern void SpotToneOff(SpotToneGen gen); +extern BOOLEAN SpotTone(SpotToneGen gen); + +#endif diff --git a/jDttSP/thunk.c b/jDttSP/thunk.c new file mode 100644 index 0000000..e52d80a --- /dev/null +++ b/jDttSP/thunk.c @@ -0,0 +1,23 @@ +/* thunk.c */ + +#include +#include + +PRIVATE BOOLEAN +streq(char *p, char *q) { return !strcmp(p, q); } + +/* somewhere along the line + we'll kick this up a notch + with gperf */ + +Thunk +Thunk_lookup(CTB ctb, char *key) { + if (key && *key) { + for (;;) { + if (!ctb || !ctb->key || !ctb->thk) break; + if (streq(key, ctb->key)) return ctb->thk; + ctb++; + } + } + return (Thunk) 0; +} diff --git a/jDttSP/thunk.h b/jDttSP/thunk.h new file mode 100644 index 0000000..9a16c32 --- /dev/null +++ b/jDttSP/thunk.h @@ -0,0 +1,12 @@ +/* thunk.c */ + +#ifndef _thunk_h +#define _thunk_h + +typedef int (*Thunk)(int n, char **p); + +typedef struct _cmd_tbl_entry { char *key; Thunk thk; } CTE, *CTB; + +extern Thunk Thunk_lookup(CTB ctb, char *key); + +#endif diff --git a/jDttSP/update.c b/jDttSP/update.c new file mode 100644 index 0000000..6d2594a --- /dev/null +++ b/jDttSP/update.c @@ -0,0 +1,760 @@ +/* update.c + +common defs and code for parm update + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//////////////////////////////////////////////////////////////////////////// + +PRIVATE REAL +dB2lin(REAL dB) { return pow(10.0, dB / 20.0); } + +PRIVATE int +setRXFilter(int n, char **p) { + REAL low_frequency = atof(p[0]), + high_frequency = atof(p[1]); + int ncoef = uni.buflen + 1; + int i, fftlen = 2 * uni.buflen; + fftw_plan ptmp; + COMPLEX *zcvec; + + if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; + if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; + if ((low_frequency + 10) >= high_frequency) return -3; + delFIR_COMPLEX(rx.filt.coef); + + rx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = rx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = rx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) rx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + normalize_vec_COMPLEX(rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen); + memcpy((char *) rx.filt.save, + (char *) rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + return 0; +} + + +PRIVATE int +setTXFilter(int n, char **p) { + REAL low_frequency = atof(p[0]), + high_frequency = atof(p[1]); + int ncoef = uni.buflen + 1; + int i, fftlen = 2 * uni.buflen; + fftw_plan ptmp; + COMPLEX *zcvec; + + if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; + if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; + if ((low_frequency + 10) >= high_frequency) return -3; + delFIR_COMPLEX(tx.filt.coef); + tx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = tx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) tx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + normalize_vec_COMPLEX(tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen); + memcpy((char *) tx.filt.save, + (char *) tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + return 0; +} + +PRIVATE int +setFilter(int n, char **p) { + if (n == 2) return setRXFilter(n, p); + else { + int trx = atoi(p[2]); + if (trx == RX) return setRXFilter(n, p); + else if (trx == TX) return setTXFilter(n, p); + else return -1; + } +} + +// setMode [TRX] + +PRIVATE int +setMode(int n, char **p) { + int mode = atoi(p[0]); + if (n > 1) { + int trx = atoi(p[1]); + switch (trx) { + case TX: tx.mode = mode; break; + case RX: + default: rx.mode = mode; break; + } + } else + tx.mode = rx.mode = uni.mode.sdr = mode; + if (rx.mode == AM) rx.am.gen->mode = AMdet; + if (rx.mode == SAM) rx.am.gen->mode = SAMdet; + return 0; +} + +// setOsc [TRX] +PRIVATE int +setOsc(int n, char **p) { + REAL newfreq = atof(p[0]); + if (fabs(newfreq) >= 0.5 * uni.samplerate) return -1; + newfreq *= 2.0 * M_PI / uni.samplerate; + if (n > 1) { + int trx = atoi(p[1]); + switch (trx) { + case TX: tx.osc.gen->Frequency = newfreq; break; + case RX: + default: rx.osc.gen->Frequency = newfreq; break; + } + } else + tx.osc.gen->Frequency = rx.osc.gen->Frequency = newfreq; + return 0; +} + +PRIVATE int +setSampleRate(int n, char **p) { + REAL samplerate = atof(p[0]); + uni.samplerate = samplerate; + return 0; +} + +PRIVATE int +setNR(int n, char **p) { + rx.anr.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setANF(int n, char **p) { + rx.anf.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setNB(int n, char **p) { + rx.nb.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setNBvals(int n, char **p) { + REAL threshold = atof(p[0]); + rx.nb.gen->threshold = rx.nb.thresh = threshold; + return 0; +} + +PRIVATE int +setSDROM(int n, char **p) { + rx.nb_sdrom.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setSDROMvals(int n, char **p) { + REAL threshold = atof(p[0]); + rx.nb_sdrom.gen->threshold = rx.nb_sdrom.thresh = threshold; + return 0; +} + +PRIVATE int +setBIN(int n, char **p) { + rx.bin.flag = atoi(p[0]); + return 0; +} + +// setfixedAGC [TRX] +PRIVATE int +setfixedAGC(int n, char **p) { + REAL gain = atof(p[0]); + if (n > 1) { + int trx = atoi(p[1]); + switch(trx) { + case TX: tx.agc.gen->gain.now = gain; break; + case RX: + default: rx.agc.gen->gain.now = gain; break; + } + } else + tx.agc.gen->gain.now = rx.agc.gen->gain.now = gain; + return 0; +} + +PRIVATE int +setRXAGC(int n, char **p) { + int setit = atoi(p[0]); + switch (setit) { + case agcOFF: + rx.agc.gen->mode = agcOFF; + rx.agc.flag = TRUE; + break; + case agcSLOW: + rx.agc.gen->mode = agcSLOW; + rx.agc.gen->hang = 10; + rx.agc.flag = TRUE; + break; + case agcMED: + rx.agc.gen->mode = agcMED; + rx.agc.gen->hang = 6; + rx.agc.flag = TRUE; + break; + case agcFAST: + rx.agc.gen->mode = agcFAST; + rx.agc.gen->hang = 3; + rx.agc.flag = TRUE; + break; + case agcLONG: + rx.agc.gen->mode = agcLONG; + rx.agc.gen->hang = 23; + rx.agc.flag = TRUE; + break; + } + return 0; +} + +PRIVATE int +setRXAGCCompression(int n, char **p) { + REAL rxcompression = atof(p[0]); + rx.agc.gen->gain.top = pow(10.0 , rxcompression * 0.05); + return 0; +} + +PRIVATE int +setRXAGCHang(int n, char **p) { + int hang = atoi(p[0]); + rx.agc.gen->hang = + max(1, + min(23, + hang * uni.samplerate / (1e3 * uni.buflen))); + return 0; +} + +PRIVATE int +setRXAGCLimit(int n, char **p) { + REAL limit = atof(p[0]); + rx.agc.gen->gain.lim = 0.001 * limit; + return 0; +} + +PRIVATE int +setTXAGC(int n, char **p) { + int setit = atoi(p[0]); + switch (setit) { + case agcOFF: + tx.agc.gen->mode = agcOFF; + tx.agc.flag = FALSE; + break; + case agcSLOW: + tx.agc.gen->mode = agcSLOW; + tx.agc.gen->hang = 10; + tx.agc.flag = TRUE; + break; + case agcMED: + tx.agc.gen->mode = agcMED; + tx.agc.gen->hang = 6; + tx.agc.flag = TRUE; + break; + case agcFAST: + tx.agc.gen->mode = agcFAST; + tx.agc.gen->hang = 3; + tx.agc.flag = TRUE; + break; + case agcLONG: + tx.agc.gen->mode = agcLONG; + tx.agc.gen->hang = 23; + tx.agc.flag = TRUE; + break; + } + return 0; +} + +PRIVATE int +setTXAGCCompression(int n, char **p) { + REAL txcompression = atof(p[0]); + tx.agc.gen->gain.top = pow(10.0 , txcompression * 0.05); + return 0; +} + +PRIVATE int +setTXAGCFF(int n, char **p) { + tx.spr.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXAGCFFCompression(int n, char **p) { + REAL txcompression = atof(p[0]); + tx.spr.gen->MaxGain = pow(10.0 , txcompression * 0.05); + return 0; +} + +PRIVATE int +setTXAGCHang(int n, char **p) { + int hang = atoi(p[0]); + tx.agc.gen->hang = + max(1, + min(23, + hang * uni.samplerate / (1e3 * uni.buflen))); + return 0; +} + +PRIVATE int +setTXAGCLimit(int n, char **p) { + REAL limit = atof(p[0]); + tx.agc.gen->gain.lim = 0.001 * limit; + return 0; +} + +PRIVATE int +setTXSpeechCompression(int n, char **p) { + tx.spr.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXSpeechCompressionGain(int n, char **p) { + tx.spr.gen->MaxGain = dB2lin(atof(p[0])); + return 0; +} + +//============================================================ + +PRIVATE int +f2x(REAL f) { + REAL fix = tx.filt.ovsv->fftlen * f / uni.samplerate; + return (int) (fix + 0.5); +} + +//------------------------------------------------------------ + +PRIVATE void +apply_txeq_band(REAL lof, REAL dB, REAL hif) { + int i, + lox = f2x(lof), + hix = f2x(hif), + l = tx.filt.ovsv->fftlen; + REAL g = dB2lin(dB); + COMPLEX *src = tx.filt.save, + *trg = tx.filt.ovsv->zfvec; + for (i = lox; i < hix; i++) { + trg[i] = Cscl(src[i], g); + if (i) { + int j = l - i; + trg[j] = Cscl(src[j], g); + } + } +} + +// typical: +// 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600 +// approximates W2IHY bandcenters. +// no args, revert to no EQ. +PRIVATE int +setTXEQ(int n, char **p) { + if (n < 3) { + // revert to no EQ + memcpy((char *) tx.filt.ovsv->zfvec, + (char *) tx.filt.save, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + return 0; + } else { + int i; + REAL lof = atof(p[0]); + for (i = 0; i < n - 2; i += 2) { + REAL dB = atof(p[i + 1]), + hif = atof(p[i + 2]); + if (lof < 0.0 || hif <= lof) return -1; + apply_txeq_band(lof, dB, hif); + lof = hif; + } + return 0; + } +} + +//------------------------------------------------------------ + +PRIVATE void +apply_rxeq_band(REAL lof, REAL dB, REAL hif) { + int i, + lox = f2x(lof), + hix = f2x(hif), + l = rx.filt.ovsv->fftlen; + REAL g = dB2lin(dB); + COMPLEX *src = rx.filt.save, + *trg = rx.filt.ovsv->zfvec; + for (i = lox; i < hix; i++) { + trg[i] = Cscl(src[i], g); + if (i) { + int j = l - i; + trg[j] = Cscl(src[j], g); + } + } +} + +PRIVATE int +setRXEQ(int n, char **p) { + if (n < 3) { + // revert to no EQ + memcpy((char *) rx.filt.ovsv->zfvec, + (char *) rx.filt.save, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + return 0; + } else { + int i; + REAL lof = atof(p[0]); + for (i = 0; i < n - 2; i += 2) { + REAL dB = atof(p[i + 1]), + hif = atof(p[i + 2]); + if (lof < 0.0 || hif <= lof) return -1; + apply_rxeq_band(lof, dB, hif); + lof = hif; + } + return 0; + } +} + +//============================================================ + +PRIVATE int +setANFvals(int n, char **p) { + int taps = atoi(p[0]), + delay = atoi(p[1]); + REAL gain = atof(p[2]), + leak = atof(p[3]); + rx.anf.gen->adaptive_filter_size = taps; + rx.anf.gen->delay = delay; + rx.anf.gen->adaptation_rate = gain; + rx.anf.gen->leakage = leak; + return 0; +} + +PRIVATE int +setNRvals(int n, char **p) { + int taps = atoi(p[0]), + delay = atoi(p[1]); + REAL gain = atof(p[2]), + leak = atof(p[3]); + rx.anr.gen->adaptive_filter_size = taps; + rx.anr.gen->delay = delay; + rx.anr.gen->adaptation_rate = gain; + rx.anr.gen->leakage = leak; + return 0; +} + +PRIVATE int +setcorrectIQ(int n, char **p) { + int phaseadjustment = atoi(p[0]), + gainadjustment = atoi(p[1]); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + rx.iqfix->gain = 1.0+ 0.001 * (REAL) gainadjustment; + return 0; +} + +PRIVATE int +setcorrectIQphase(int n, char **p) { + int phaseadjustment = atoi(p[0]); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + return 0; +} + +PRIVATE int +setcorrectIQgain(int n, char **p) { + int gainadjustment = atoi(p[0]); + rx.iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment; + return 0; +} + +PRIVATE int +setSquelch(int n, char **p) { + rx.squelch.thresh = -atof(p[0]); + return 0; +} + +PRIVATE int +setMeterOffset(int n, char **p) { + rx.squelch.offset.meter = atof(p[0]); + return 0; +} + +PRIVATE int +setATTOffset(int n, char **p) { + rx.squelch.offset.att = atof(p[0]); + return 0; +} + +PRIVATE int +setGainOffset(int n, char **p) { + rx.squelch.offset.gain = atof(p[0]); + return 0; +} + +PRIVATE int +setSquelchSt(int n, char **p) { + rx.squelch.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTRX(int n, char **p) { + uni.mode.trx = atoi(p[0]); + return 0; +} + +PRIVATE int +setRunState(int n, char **p) { + RUNMODE rs = (RUNMODE) atoi(p[0]); + top.state = rs; + return 0; +} + +PRIVATE int +setSpotToneVals(int n, char **p) { + REAL gain = atof(p[0]), + freq = atof(p[1]), + rise = atof(p[2]), + fall = atof(p[3]); + setSpotToneGenVals(rx.spot.gen, gain, freq, rise, fall); + return 0; +} + +PRIVATE int +setSpotTone(int n, char **p) { + if (atoi(p[0])) { + SpotToneOn(rx.spot.gen); + rx.spot.flag = TRUE; + } else + SpotToneOff(rx.spot.gen); + return 0; +} + +PRIVATE int +setRXPreScl(int n, char **p) { + rx.scl.pre.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setRXPreSclVal(int n, char **p) { + rx.scl.pre.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setTXPreScl(int n, char **p) { + tx.scl.pre.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXPreSclVal(int n, char **p) { + tx.scl.pre.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setRXPostScl(int n, char **p) { + rx.scl.post.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setRXPostSclVal(int n, char **p) { + rx.scl.post.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setTXPostScl(int n, char **p) { + tx.scl.post.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXPostSclVal(int n, char **p) { + tx.scl.post.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setFinished(int n, char **p) { + top.running = FALSE; + pthread_cancel(top.thrd.trx.id); + pthread_cancel(top.thrd.upd.id); + pthread_cancel(top.thrd.mon.id); + return 0; +} + +// next-trx-mode [nbufs-to-zap] +PRIVATE int +setSWCH(int n, char **p) { + top.swch.trx.next = atoi(p[0]); + if (n > 1) top.swch.bfct.want = atoi(p[1]); + else top.swch.bfct.want = 0; + top.swch.bfct.have = 0; + top.swch.run.last = top.state; + top.state = RUN_SWCH; + return 0; +} + +PRIVATE int +setMonDump(int n, char **p) { + sem_post(&top.sync.mon.sem); + return 0; +} + +PRIVATE int +setRingBufferReset(int n, char **p) { + extern void clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes); + jack_ringbuffer_reset(top.jack.ring.i.l); + jack_ringbuffer_reset(top.jack.ring.i.r); + jack_ringbuffer_reset(top.jack.ring.o.l); + jack_ringbuffer_reset(top.jack.ring.o.r); + clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes); + clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes); + return 0; +} + +//======================================================================== + +#include + +CTE update_cmds[] = { + {"setANF", setANF}, + {"setANFvals", setANFvals}, + {"setATTOffset", setATTOffset}, + {"setBIN", setBIN}, + {"setFilter", setFilter}, + {"setFinished", setFinished}, + {"setGainOffset", setGainOffset}, + {"setMeterOffset", setMeterOffset}, + {"setMode", setMode}, + {"setNB", setNB}, + {"setNBvals", setNBvals}, + {"setNR", setNR}, + {"setNRvals", setNRvals}, + {"setOsc", setOsc}, + {"setRXAGC", setRXAGC}, + {"setRXAGCCompression", setRXAGCCompression}, + {"setRXAGCHang", setRXAGCHang}, + {"setRXAGCLimit", setRXAGCLimit}, + {"setRXEQ", setRXEQ}, + {"setRXPostScl", setRXPostScl}, + {"setRXPostSclVal", setRXPostSclVal}, + {"setRXPreScl", setRXPreScl}, + {"setRXPreSclVal", setRXPreSclVal}, + {"setRunState", setRunState}, + {"setSDROM", setSDROM}, + {"setSDROMvals",setSDROMvals}, + {"setSWCH", setSWCH}, + {"setSampleRate", setSampleRate}, + {"setSpotTone", setSpotTone}, + {"setSpotToneVals", setSpotToneVals}, + {"setSquelch", setSquelch}, + {"setSquelchSt", setSquelchSt}, + {"setTRX", setTRX}, + {"setTXAGC", setTXAGC}, + {"setTXAGCCompression", setTXAGCCompression}, + {"setTXAGCFFCompression",setTXAGCFFCompression}, + {"setTXAGCHang", setTXAGCHang}, + {"setTXAGCLimit", setTXAGCLimit}, + {"setTXEQ", setTXEQ}, + {"setTXPostScl", setTXPostScl}, + {"setTXPostSclVal", setTXPostSclVal}, + {"setTXPreScl", setTXPreScl}, + {"setTXPreSclVal", setTXPreSclVal}, + {"setTXSpeechCompression", setTXSpeechCompression}, + {"setTXSpeechCompressionGain", setTXSpeechCompressionGain}, + {"setcorrectIQ", setcorrectIQ}, + {"setcorrectIQgain", setcorrectIQgain}, + {"setcorrectIQphase", setcorrectIQphase}, + {"setfixedAGC", setfixedAGC}, + {"setMonDump", setMonDump}, + {"setRingBufferReset", setRingBufferReset}, + { 0, 0 } +}; + +//........................................................................ + +int +do_update(char *str, FILE *log) { + SPLIT splt = &uni.update.splt; + + split(splt, str); + + if (NF(splt) < 1) return -1; + + else { + Thunk thk = Thunk_lookup(update_cmds, F(splt, 0)); + if (!thk) return -1; + else { + int val; + + sem_wait(&top.sync.upd.sem); + val = (*thk)(NF(splt) - 1, Fptr(splt, 1)); + sem_post(&top.sync.upd.sem); + + if (log) { + int i; + char *s = since(&top.start_tv); + fprintf(log, "update[%s]: returned %d from", s, val); + for (i = 0; i < NF(splt); i++) + fprintf(log, " %s", F(splt, i)); + putc('\n', log); + fflush(log); + } + + return val; + } + } +} diff --git a/jDttSP/update.h b/jDttSP/update.h new file mode 100644 index 0000000..b0de3c7 --- /dev/null +++ b/jDttSP/update.h @@ -0,0 +1,45 @@ +/* update.h + +common defs and code for parm update + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _update_h +#define _update_h + +#include +#include +#include + +extern int do_update(char *str, FILE *log); + +#endif diff --git a/jDttSP/win/banal.c b/jDttSP/win/banal.c new file mode 100644 index 0000000..154ba40 --- /dev/null +++ b/jDttSP/win/banal.c @@ -0,0 +1,152 @@ +/* banal.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include +#include + +int +in_blocks(int count, int block_size) { + if (block_size < 1) { + fprintf(stderr, "block_size zero in in_blocks\n"); + exit(1); + } + return (1 + ((count - 1) / block_size)); +} + +FILE * +efopen(char *path, char *mode) { + FILE *iop = fopen(path, mode); + if (!iop) { + fprintf(stderr, "can't open \"%s\" in mode \"%s\"\n", path, mode); + exit(1); + } + return iop; +} + +FILE * +efreopen(char *path, char *mode, FILE *strm) { + FILE *iop = freopen(path, mode, strm); + if (!iop) { + fprintf(stderr, "can't reopen \"%s\" in mode \"%s\"\n", path, mode); + exit(1); + } + return iop; +} + +size_t +filesize(char *path) { + struct stat sbuf; + if (stat(path, &sbuf) == -1) return -1; + return sbuf.st_size; +} + +size_t +fdsize(int fd) { + struct stat sbuf; + if (fstat(fd, &sbuf) == -1) return -1; + return sbuf.st_size; +} + +#define MILLION (1000000) +#ifndef _WINDOWS +// return current tv +struct timeval +now_tv(void) { + struct timeval tv; + gettimeofday(&tv, 0); + return tv; +} + +// return ta - tb +struct timeval +diff_tv(struct timeval *ta, struct timeval *tb) { + struct timeval tv; + if (tb->tv_usec > ta->tv_usec) { + ta->tv_sec--; + ta->tv_usec += MILLION; + } + tv.tv_sec = ta->tv_sec - tb->tv_sec; + if ((tv.tv_usec = ta->tv_usec - tb->tv_usec) >= MILLION) { + tv.tv_usec -= MILLION; + tv.tv_sec++; + } + return tv; +} + +// return ta + tb +struct timeval +sum_tv(struct timeval *ta, struct timeval *tb) { + struct timeval tv; + tv.tv_sec = ta->tv_sec + tb->tv_sec; + if ((tv.tv_usec = ta->tv_usec + tb->tv_usec) >= MILLION) { + tv.tv_usec -= MILLION; + tv.tv_sec++; + } + return tv; +} + +char * +fmt_tv(struct timeval *tv) { +#ifndef _WINDOWS + static char buff[256]; + snprintf(buff, sizeof(buff), "%ds%du", tv->tv_sec, tv->tv_usec); +#else + static char buff[512]; + sprintf(buff, "%ds%du", tv->tv_sec, tv->tv_usec); +#endif + return buff; +} + +char * +since(struct timeval *tv) { + struct timeval nt = now_tv(), + dt = diff_tv(&nt, tv); + return fmt_tv(&dt); +} +#endif + +int +hinterp_vec(REAL *u, int m, REAL *v, int n) { + if (!u || !v || (n < 2) || (m < n) || (m % n)) return 0; + else { + int div = m / n, i, j = 0; + for (i = 1; i < n; i++) { + int k; + REAL vl = v[i - 1], del = (v[i] - vl) / div; + u[j++] = vl; + for (k = 1; k < div; k++) u[j++] = vl + k * del; + } + u[j++] = v[n - 1]; + return j; + } +} diff --git a/jDttSP/win/banal.h b/jDttSP/win/banal.h new file mode 100644 index 0000000..4e4380d --- /dev/null +++ b/jDttSP/win/banal.h @@ -0,0 +1,95 @@ +/* banal.h + stuff we're too embarrassed to declare otherwise + + This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _banal_h + +#define _banal_h + +#include +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#define abs(a) ((a) >= 0 ? (a) : -(a)) + +#define MONDO 1e15 +#define BITSY 1e-15 + +#define TRUE 1 +#define FALSE 0 + +_inline void +nilfunc(void) {} + +_inline double +sqr(double x) { return x * x; } + +_inline int +popcnt(int k) { + int c, i; + c = k & 01; + for (i = 1; i < 32; i++) c += (k >> i) & 01; + return c; +} + +_inline int +npoof2(int n) { + int i = 0; + --n; + while (n > 0) n >>= 1, i++; + return i; +} + +_inline int +nblock2(int n) { return 1 << npoof2(n); } + +extern int in_blocks(int count, int block_size); +extern FILE *efopen(char *path, char *mode); +extern FILE *efreopen(char *path, char *mode, FILE *strm); +extern size_t filesize(char *path); +extern size_t fdsize(int fd); +#ifndef _WINDOWS +extern struct timeval now_tv(void); +extern struct timeval diff_tv(struct timeval *, struct timeval *); +extern struct timeval sum_tv(struct timeval *, struct timeval *); +extern char *fmt_tv(struct timeval *); +extern char *since(struct timeval *); +extern struct timeval now_tv(void); +#endif +extern int hinterp_vec(REAL *, int, REAL *, int); +#endif + + diff --git a/jDttSP/win/chan.c b/jDttSP/win/chan.c new file mode 100644 index 0000000..68889b0 --- /dev/null +++ b/jDttSP/win/chan.c @@ -0,0 +1,113 @@ +/* chan.c + +fast inter-user-process single-reader/single-writer channels +using a modified version of jack ringbuffers and memory-mapped files. + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//------------------------------------------------------------------------ + +size_t +putChan(Chan c, char *data, size_t size) { + return ringb_write(c->rb, data, size); +} + +size_t +getChan(Chan c, char *data, size_t size) { + return ringb_read(c->rb, data, size); +} + +BOOLEAN +putChan_nowait(Chan c, char *data, size_t size) { + if (ringb_write_space(c->rb) >= size) { + ringb_write(c->rb, data, size); + return TRUE; + } else return FALSE; +} + +BOOLEAN +getChan_nowait(Chan c, char *data, size_t size) { + if (ringb_read_space(c->rb) >= size) { + ringb_read(c->rb, data, size); + return TRUE; + } else return FALSE; +} + +Chan +openChan(char *path,size_t want) { + Chan c = (Chan) safealloc(sizeof(ChanDesc), 1, "Chan header"); + c->size.rib = sizeof(ringb_t); + c->size.buf = (size_t) nblock2((int) want); +#ifndef _WINDOWS + c->file.path = path; + if ((c->file.desc = open(c->file.path, O_RDWR)) == -1) { + fprintf(stderr, "can't open Chan file %s\n", c->file.path); + exit(1); + } + c->size.tot = c->size.rib + c->size.buf; + if ((c->size.tru = fdsize(c->file.desc)) < c->size.tot) { + fprintf(stderr, + "Chan file %s is too small (%d) for required size (%s)\n", + c->file.path, c->size.tru, c->size.tot); + exit(1); + } + if (!(c->map.base = (char *) mmap(0, + c->size.tot, + PROT_READ | PROT_WRITE, + MAP_SHARED, + c->file.desc, + 0))) { + fprintf(stderr, "can't memory map %s for Chan\n", + c->file.path); + exit(1); + } +#endif + if (!(c->rb = ringb_create(c->size.buf))) { + fprintf(stderr, "can't make RB for Chan\n"); + exit(1); + } + return c; +} + +void +closeChan(Chan c) { + if (c) { +#ifndef _WINDOWS + munmap(c->map.base, c->size.tot); + close(c->file.desc); +#endif + ringb_destroy(c->rb); + safefree((char *) c); + } +} diff --git a/jDttSP/win/chan.h b/jDttSP/win/chan.h new file mode 100644 index 0000000..eecc507 --- /dev/null +++ b/jDttSP/win/chan.h @@ -0,0 +1,77 @@ +/* chan.h + +fast inter-user-process single-reader/single-writer channels +using a modified version of jack ringbuffers and memory-mapped files. + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _chan_h +#define _chan_h + +#include +#include +#include +#include +#ifndef _WINDOWS +#include +#else +#include +#endif +#include +#include +#include +#include + + +typedef +struct _chan { + struct { + size_t buf, rib, tot, tru; + } size; + struct { + char *path; + int desc; + } file; + struct { + char *base; + } map; + ringb_t *rb; +} ChanDesc, *Chan; + +extern size_t putChan(Chan c, char *data, size_t size); +extern size_t getChan(Chan c, char *data, size_t size); +extern BOOLEAN putChan_nowait(Chan c, char *data, size_t size); +extern BOOLEAN getChan_nowait(Chan c, char *data, size_t size); +extern Chan openChan(char *path,size_t want); +extern void closeChan(Chan c); + +#endif diff --git a/jDttSP/win/common.h b/jDttSP/win/common.h new file mode 100644 index 0000000..09121a2 --- /dev/null +++ b/jDttSP/win/common.h @@ -0,0 +1,78 @@ +/* common.h +a simple way to get all the necessary includes + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _common_h + +#define _common_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WINDOWS +#include +#include +typedef long pid_t; +typedef long uid_t; +typedef LPDWORD pthread_t; +typedef HANDLE sem_t; +#define DLLVERS +//#define PROCESSVERS +#define NEWSPLIT +#endif + +#include +#endif + diff --git a/jDttSP/win/datatypes.h b/jDttSP/win/datatypes.h new file mode 100644 index 0000000..74b3dc6 --- /dev/null +++ b/jDttSP/win/datatypes.h @@ -0,0 +1,56 @@ +/* datatypes.h + local definitions and aliases for our data +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _datatypes_h + +#define _datatypes_h + +#include +#include +#ifndef _WINDOWS +typedef int BOOLEAN; +#else +typedef unsigned char BOOLEAN; +#endif +typedef double REAL; +typedef double IMAG; +typedef short SAMPLE_16t; + +#include + +#ifndef PRIVATE +#define PRIVATE static +#endif + +#endif + diff --git a/jDttSP/win/fromsys.h b/jDttSP/win/fromsys.h new file mode 100644 index 0000000..c943da9 --- /dev/null +++ b/jDttSP/win/fromsys.h @@ -0,0 +1,95 @@ +/* fromsys.h + stuff we need to import everywhere + This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _fromsys_h +#define _fromsys_h +#ifndef _WINDOWS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#define DttSP_EXP + +#else + +#include +//#include // WINBLOWS +#define MAXPATHLEN (260-1 /* NULL */) +#include +#include +//#include +#include + +#include +#include +#include +#include +#include +#include +#ifndef M_PI +#define M_PI 3.14159265358928 +#endif +#include +#define DttSP_EXP __declspec(dllexport) +#define DttSP_IMP __declspec(dllimport) + +#define sem_wait(p) WaitForSingleObject(p,INFINITE) +#define sem_post(p) ReleaseSemaphore(p,1,NULL); +#define pthread_exit(p) ExitThread((DWORD)p) + +#endif // _WINDOWS +#endif // _fromsys_h diff --git a/jDttSP/win/main.c b/jDttSP/win/main.c new file mode 100644 index 0000000..96c3cda --- /dev/null +++ b/jDttSP/win/main.c @@ -0,0 +1,534 @@ +/* main.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +///////////////////////////////////////////////////////////////////////// +extern void process_samples(float *, float *, int); +extern void setup_workspace(void); + +#ifdef _WINDOWS + +#define PARMPATH NULL +#define RINGMULT (4) +#define METERPATH NULL +#define METERMULT (2) +#define PWSPATH NULL +#define PWSMULT (32768) +#define TEST + +#else + +#define PARMPATH "./IPC/SDR-1000-0-commands.fifo" +#define RINGMULT (4) +#define METERPATH "./IPC/SDR-1000-0-meter.chan" +#define METERMULT (2) +#define PWSPATH "./IPC/SDR-1000-0-pws.chan" +#define PWSMULT (32768) + +#endif +//------------------------------------------------------------------------ + +#ifdef _WINDOWS +PRIVATE DWORD WINAPI +process_updates_thread(void) { + size_t tmp; + while (top.running) { + while (newupdates()) { + tmp = ringb_read_space(uni.update.chan.c->rb); + getChan_nowait(uni.update.chan.c, top.parm.buff, tmp); + do_update(top.parm.buff); + } + } + ExitThread(0); +} + +//======================================================================== + +PRIVATE void +gethold(void) { + ringb_write(top.jack.ring.o.l, (char *) top.hold.buf.l, top.hold.size.bytes); + ringb_write(top.jack.ring.o.r, (char *) top.hold.buf.r, top.hold.size.bytes); + ringb_read(top.jack.ring.i.l, (char *) top.hold.buf.l, top.hold.size.bytes); + ringb_read(top.jack.ring.i.r, (char *) top.hold.buf.r, top.hold.size.bytes); +} + +PRIVATE BOOLEAN canhold(void) { + return ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes; +} + + +#else + +//======================================================================== + +PRIVATE void +process_updates_thread(void) { + while (top.running) { + while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp)) + do_update(top.parm.buff, top.verbose ? stderr : 0); + } + pthread_exit(0); +} + +PRIVATE void +gethold(void) { + jack_ringbuffer_write(top.jack.ring.o.l, (char *) top.hold.buf.l, top.hold.size.bytes); + jack_ringbuffer_write(top.jack.ring.o.r, (char *) top.hold.buf.r, top.hold.size.bytes); + jack_ringbuffer_read(top.jack.ring.i.l, (char *) top.hold.buf.l, top.hold.size.bytes); + jack_ringbuffer_read(top.jack.ring.i.r, (char *) top.hold.buf.r, top.hold.size.bytes); +} + +PRIVATE BOOLEAN canhold(void) { + return jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes; +} + +#endif + +PRIVATE void +run_mute(void) { + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); +} + +PRIVATE void +run_pass(void) {} + +PRIVATE void +run_play(void) { + process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames); +} + +// NB do not set RUN_SWCH directly via setRunState; +// use setSWCH instead + +PRIVATE void +run_swch(void) { + if (top.swch.bfct.have == 0) { + // first time + // apply ramp down + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) 1.0 - (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + memset((char *) (top.hold.buf.l + m), 0, n); + memset((char *) (top.hold.buf.r + m), 0, n); + top.swch.bfct.have++; + } else if (top.swch.bfct.have < top.swch.bfct.want) { + // in medias res + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + top.swch.bfct.have++; + } else { + // last time + // apply ramp up + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + uni.mode.trx = top.swch.trx.next; + top.state = top.swch.run.last; + top.swch.bfct.want = top.swch.bfct.have = 0; + } + + process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames); +} + +//======================================================================== + +#ifdef _WINDOWS + +static int exchptr = 0; +DttSP_EXP void +exchange_samples(float *input, float *output, int nframes) { + int i, j; + for (i = 0, j = 0; i < nframes; i++, j += 2) { + ringb_read(top.jack.ring.o.l, (char *) &output[j], sizeof(float)); + ringb_read(top.jack.ring.o.r, (char *) &output[j + 1], sizeof(float)); + ringb_write(top.jack.ring.i.l, (char *) &input[j], sizeof(float)); + ringb_write(top.jack.ring.i.r, (char *) &input[j + 1], sizeof(float)); + } + if (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes) + ReleaseSemaphore(top.sync.buf.sem, 1L, NULL); +} + +DttSP_EXP void +audioreset(void) { exchptr = 0; } + +PRIVATE DWORD WINAPI +process_samples_thread(void) { + while (top.running) { + WaitForSingleObject(top.sync.buf.sem, INFINITE); + WaitForSingleObject(top.sync.upd.sem, INFINITE); + do { + gethold(); + switch (top.state) { + case RUN_MUTE: + run_mute(); + break; + case RUN_PASS: + run_pass(); + break; + case RUN_PLAY: + run_play(); + break; + case RUN_SWCH: + run_swch(); + break; + } + } while (canhold()); + ReleaseSemaphore(top.sync.upd.sem, 1L, NULL); + } + ExitThread(0); + return 1; +} + +#else /* */ + +PRIVATE void +audio_callback(jack_nframes_t nframes, void *arg) { + float *lp, *rp; + int nbytes = nframes * sizeof(float); + + // output: copy from ring to port + lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes); + rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes); + jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes); + jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes); + + // input: copy from port to ring + lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes); + rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes); + jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes); + jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes); + + // if enough accumulated in ring, fire dsp + if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes) + sem_post(&top.sync.buf.sem); +} + +PRIVATE void +process_samples_thread(void) { + while (top.running) { + sem_wait(&top.sync.buf.sem); + do { + gethold(); + sem_wait(&top.sync.upd.sem); + switch (top.state) { + case RUN_MUTE: + run_mute(); + break; + case RUN_PASS: + run_pass(); + break; + case RUN_PLAY: + run_play(); + break; + case RUN_SWCH: + run_swch(); + break; + } + sem_post(&top.sync.upd.sem); + } while (canhold()); + } + pthread_exit(0); +} + +#endif + +//======================================================================== + +#ifdef _WINDOWS +DttSP_EXP void +execute(void) { + ResumeThread(top.thrd.trx.thrd); +} + +DttSP_EXP void +closeup(void) { + extern void destroy_workspace(); + TerminateThread(top.thrd.trx.thrd, 0L); + CloseHandle(top.thrd.trx.thrd); + CloseHandle(top.thrd.upd.thrd); + CloseHandle(top.sync.buf.sem); + CloseHandle(top.sync.upd.sem); + destroy_workspace(); +} + +#else + +PRIVATE void +execute(void) { + sem_post(&top.sync.upd.sem); + + // start audio processing + if (jack_activate(top.jack.client)) + perror("cannot activate jack client"), exit(1); + + // wait for threads to terminate + pthread_join(top.thrd.trx.id, 0); + pthread_join(top.thrd.upd.id, 0); + + // stop audio processing + jack_client_close(top.jack.client); +} + +PRIVATE void +closeup(void) { exit(0); } + +PRIVATE void +usage(void) { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "-m\n"); + fprintf(stderr, "-v\n"); + exit(1); +} + +PRIVATE void +nonblock(int fd) { + long arg; + arg = fcntl(fd, F_GETFL); + arg |= O_NONBLOCK; +/* if (fcntl(fd, F_GETFL, &arg) >= 0) + fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ + fcntl(fd, F_SETFL, arg); +} + +#endif + +//........................................................................ + +PRIVATE void +setup_meter(void) { + uni.meter.flag = TRUE; + uni.meter.chan.path = METERPATH; + uni.meter.chan.size = METERMULT * sizeof(REAL); + uni.meter.val = -200.0; + uni.meter.chan.c = openChan(uni.meter.chan.path, uni.meter.chan.size); +} + +PRIVATE void +setup_powerspectrum(void) { + uni.powsp.flag = TRUE; + uni.powsp.chan.path = PWSPATH; + uni.powsp.chan.size = 8192 * sizeof(COMPLEX); + uni.powsp.buf = newCXB(4096, NULL, "PowerSpectrum Chan Buffer"); + uni.powsp.fftsize = 4096; + uni.powsp.chan.c = openChan(uni.powsp.chan.path, uni.powsp.chan.size); +} + +PRIVATE void +setup_switching(void) { + top.swch.fade = (int) (0.1 * uni.buflen + 0.5); + top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float); +} + +PRIVATE void +setup_local_audio(void) { + top.hold.size.frames = uni.buflen; + top.hold.size.bytes = top.hold.size.frames * sizeof(float); + top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer left"); + top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer right"); +} + +#ifdef _WINDOWS + +PRIVATE void +setup_updates(void) { + uni.update.flag = TRUE; + uni.update.chan.path = NULL; + uni.update.chan.size = 4096; + uni.update.chan.c = openChan(uni.update.chan.path, uni.update.chan.size); +} PRIVATE void + +setup_system_audio(void) { + sprintf(top.jack.name, "sdr-%d", top.pid); + top.jack.ring.i.l = ringb_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.i.r = ringb_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.o.l = ringb_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.o.r = ringb_create(top.hold.size.bytes * RINGMULT); + { + unsigned int i; + char zero = 0; + for (i = 0; i < top.hold.size.bytes; i++) { + ringb_write(top.jack.ring.o.l, &zero, 1); + ringb_write(top.jack.ring.o.r, &zero, 1); + } + } +} + +DttSP_EXP void +release_update(void) {} + +DttSP_EXP void +suspend_sdr(void) { SuspendThread(top.thrd.trx.thrd); } + +PRIVATE void +setup_threading(void) { + top.sync.upd.sem = CreateSemaphore(NULL, 1L, 1L, "UpdateSemaphore"); + top.sync.buf.sem = CreateSemaphore(NULL, 0L, 1L, "Process Samples Semaphore"); + top.thrd.trx.thrd = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0L, process_samples_thread, + NULL, CREATE_SUSPENDED, &top.thrd.trx.id); + SetThreadPriority(top.thrd.trx.thrd, THREAD_PRIORITY_HIGHEST); +} + +DttSP_EXP void +setup_sdr(void) { + top.pid = GetCurrentThreadId(); + top.uid = 0L; + top.running = TRUE; + top.state = RUN_PLAY; + setup_meter(); + setup_powerspectrum(); + setup_updates(); + uni.meter.flag = TRUE; + top.verbose = FALSE; + setup_workspace(); + setup_local_audio(); + setup_system_audio(); + setup_threading(); + setup_switching(); +} + +#else + +PRIVATE void +setup_updates(void) { + top.parm.path = PARMPATH; + if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1) + perror(top.parm.path), exit(1); + if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) { + fprintf(stderr, "can't fdopen parm pipe %s\n", PARMPATH); + exit(1); + } +} + +PRIVATE void +setup_system_audio(void) { + sprintf(top.jack.name, "sdr-%d", top.pid); + if (!(top.jack.client = jack_client_new(top.jack.name))) + perror("can't make client -- jack not running?"), exit(1); + jack_set_process_callback(top.jack.client, (void *) audio_callback, 0); + top.jack.port.i.l = jack_port_register(top.jack.client, + "il", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + top.jack.port.i.r = jack_port_register(top.jack.client, + "ir", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + top.jack.port.o.l = jack_port_register(top.jack.client, + "ol", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + top.jack.port.o.r = jack_port_register(top.jack.client, + "or", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT); + top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT); + { + int i; + char zero = 0; + for (i = 0; i < top.hold.size.bytes * RINGMULT / 2; i++) { + jack_ringbuffer_write(top.jack.ring.o.l, &zero, 1); + jack_ringbuffer_write(top.jack.ring.o.r, &zero, 1); + } + } +} + +PRIVATE void +setup_threading(void) { + sem_init(&top.sync.upd.sem, 0, 0); + pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL); + sem_init(&top.sync.buf.sem, 0, 0); + pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL); +} + +//======================================================================== + +PRIVATE void +setup(int argc, char **argv) { + int i; + top.pid = getpid(); + top.uid = getuid(); + top.start_tv = now_tv(); + top.running = TRUE; + top.verbose = FALSE; + top.state = RUN_PLAY; + setup_meter(); + setup_updates(); + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'm': + uni.meter.flag = TRUE; + break; + case 'v': + top.verbose = TRUE; + break; + default: + usage(); + } + else break; + if (i < argc) { + if (!freopen(argv[i], "r", stdin)) + perror(argv[i]), exit(1); + i++; + } + setup_workspace(); + setup_local_audio(); + setup_system_audio(); + setup_threading(); + setup_switching(); +} + + +//======================================================================== + +int +main(int argc, char **argv) { + setup(argc, argv), execute(), closeup(); +} + +#endif diff --git a/jDttSP/win/meter.c b/jDttSP/win/meter.c new file mode 100644 index 0000000..c194fe1 --- /dev/null +++ b/jDttSP/win/meter.c @@ -0,0 +1,15 @@ +#include +#ifdef _WINDOWS + +DttSP_EXP float +calculate_meters(METERTYPE mt) { + float result; + REAL tmp; + uni.meter.type = mt; + if (getChan_nowait(uni.meter.chan.c, (char *) &tmp, sizeof(REAL)) != 0) + uni.meter.val = tmp; + result = (float) uni.meter.val; + return result; +} + +#endif diff --git a/jDttSP/win/power_spectrum.c b/jDttSP/win/power_spectrum.c new file mode 100644 index 0000000..1f1977b --- /dev/null +++ b/jDttSP/win/power_spectrum.c @@ -0,0 +1,128 @@ +#include + +#ifdef _WINDOWS + +PWS +newPowerSpectrum(int size, SPECTRUMTYPE type) { + PWS p = (PWS) safealloc(1, sizeof(powerspectrum), "New Power Spectrum"); + + p->buf.win = newRLB(size, NULL, "Power Spectrum Window Buffer"); + p->buf.res = newRLB(32768, NULL, "Save buffer"); + p->buf.wdat = newCXB(size, NULL, "Power Spectrum Windowed Signal buffer"); + p->buf.zdat = newCXB(size, NULL, "Power Spectrum FFT Results Buffer"); + p->fft.size = size; + p->flav.spec = type; + + makewindow(BLACKMANHARRIS_WINDOW, size, RLBbase(p->buf.win)); + +#ifdef notdef + { + REAL *tmp = (REAL *) alloca(size * sizeof(REAL)); + int n = size / 2, nb = n * sizeof(REAL); + memcpy(&tmp[n], RLBbase(p->buf.win), nb); + memcpy(tmp, &RLBdata(p->buf.win, n), nb); + } +#endif + + p->fft.plan = fftw_create_plan(size, FFTW_FORWARD, uni.wisdom.bits); + + return p; +} + +void +delPowerSpectrum(PWS pws) { + delCXB(pws->buf.wdat); + delCXB(pws->buf.zdat); + delRLB(pws->buf.win); + delRLB(pws->buf.win2); + fftw_destroy_plan(pws->fft.plan); + safefree((char *) pws); +} + +DttSP_EXP BOOLEAN +process_spectrum(float *results) { + int i, j, n = CXBsize(uni.powsp.buf); + + if (getChan_nowait(uni.powsp.chan.c, + (char *) CXBbase(uni.powsp.buf), + uni.powsp.fft.size * sizeof(COMPLEX)) + == TRUE) { + + // Window the data to minimize "splatter" in the powerspectrum + for (i = 0; i < uni.powsp.fft.size; i++) + CXBdata(uni.powsp.gen->buf.wdat, i) = Cscl(CXBdata(uni.powsp.buf, i), + RLBdata(uni.powsp.gen->buf.win, i)); + + // Compute the FFT + fftw_one(uni.powsp.gen->fft.plan, + (fftw_complex *) CXBbase(uni.powsp.gen->buf.wdat), + (fftw_complex *) CXBbase(uni.powsp.gen->buf.zdat)); + for (i = 0; i < n / 2; i++) { + RLBdata(uni.powsp.gen->buf.res, 8 * i) = + results[8 * i] = + (float) (10.0 * + log10(1e-160 + + Csqrmag(CXBdata(uni.powsp.gen->buf.zdat, + (i + uni.buflen))))); + RLBdata(uni.powsp.gen->buf.res, 8 * (i + uni.buflen)) = + results[8 * (i + uni.buflen)] = + (float) (10.0 * + log10(1e-160 + + Csqrmag(CXBdata(uni.powsp.gen->buf.zdat, i)))); + } + + // Interpolate for display on high resolution monitors + + for (i = 0; i < 32768; i += 8) + for (j = 1; j < 8; j++) + RLBdata(uni.powsp.gen->buf.res, i + j) = + results[i + j] = (float) 0.125 * (results[i] * (float) (8 - j) + + results[i + 8] * (float) j); + return TRUE; + } else { + for (i = 0; i < 32768; i++) + results[i] = (float) RLBdata(uni.powsp.gen->buf.res, i); + return FALSE; + } +} + +DttSP_EXP BOOLEAN +process_phase(float *results, int numpoints) { + int i, j; + if (getChan_nowait(uni.powsp.chan.c, + (char *) CXBbase(uni.powsp.buf), + uni.powsp.fft.size * sizeof(COMPLEX)) + == TRUE) { + for (i = 0, j = 0; i < numpoints; i++, j += 2) { + RLBdata(uni.powsp.gen->buf.res, j) = + results[j] = (float) CXBreal(uni.powsp.buf, i); + RLBdata(uni.powsp.gen->buf.res, j + 1) = + results[j + 1] = (float) CXBimag(uni.powsp.buf, i); + } return TRUE; + } else { + for (i = 0; i < 2 * numpoints; i++) + results[i] = (float) RLBdata(uni.powsp.gen->buf.res, i); + return FALSE; + } +} + +// Send out Real Signal, reals only + +DttSP_EXP BOOLEAN +process_scope(float *results) { + int i; + if (getChan_nowait(uni.powsp.chan.c, + (char *) CXBbase(uni.powsp.buf), + uni.powsp.fft.size * sizeof(COMPLEX)) + == TRUE) { + for (i = 0; i < uni.powsp.fft.size; i++) { + results[i] = (float) CXBreal(uni.powsp.buf, i); + } return TRUE; + } else { + for (i = 0; i < uni.powsp.fft.size; i++) + results[i] = (float) CXBreal(uni.powsp.buf, i); + return FALSE; + } +} + +#endif diff --git a/jDttSP/win/power_spectrum.h b/jDttSP/win/power_spectrum.h new file mode 100644 index 0000000..9711186 --- /dev/null +++ b/jDttSP/win/power_spectrum.h @@ -0,0 +1,53 @@ +#ifndef _power_spectrum_h +#define _power_spectrum_h + +#include +#include +#include + +#ifndef _PWSMODE +#define _PWSMODE + +typedef enum { MAG, DB } SPECTRUMTYPE; + +typedef enum _powerspectrummode { + POSTFILTER, PREFILTER, AUDIO +} PWSMODE; + +typedef enum _pws_submode { + OFF, SPECTRUM, PANADAPTER, SCOPE, PHASE, PHASE2, WATERFALL, HISTOGRAM +} PWSSUBMODE; + +typedef struct _powerspectrum { + struct { + int size; + fftw_plan plan; + } fft; + RLB WindowBuf, WindowBuf2, results; + CXB WindowedDataBuf, PowerSpectrumFFTresults; + struct { + RLB win, res; + CXB wdat, zdat; + } buf; + struct { + PWSMODE main; + PWSSUBMODE sub; + SPECTRUMTYPE spec; + } flav; +} *PWS, powerspectrum; + +typedef struct _powerspectrum { + fftw_plan pwrspecfft; + int fftsize; + RLB WindowBuf, WindowBuf2, results; + CXB WindowedDataBuf, PowerSpectrumFFTresults; + PWSMODE Mode; + PWSSUBMODE SubMode; + SPECTRUMTYPE SpectrumType; +} *PWS, powerspectrum; + +extern PWS newPowerSpectrum(int size, SPECTRUMTYPE SpectrumType); +extern void delPowerSpectrum(PWS pws); +extern void process_powerspectrum(float *results, int numpoints); + +#endif diff --git a/jDttSP/win/sdrexport.h b/jDttSP/win/sdrexport.h new file mode 100644 index 0000000..c6a337a --- /dev/null +++ b/jDttSP/win/sdrexport.h @@ -0,0 +1,346 @@ +/* sdrexport.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _sdrexport_h +#define _sdrexport_h + +#include +//------------------------------------------------------------------------ +/* modulation types, modes */ +typedef enum _sdrmode { + LSB, // 0 + USB, // 1 + DSB, // 2 + CWL, // 3 + CWU, // 4 + FMN, // 5 + AM, // 6 + PSK, // 7 + SPEC, // 8 + RTTY, // 9 + SAM, // 10 + DRM // 11 +} SDRMODE; +typedef enum _trxmode { RX, TX } TRXMODE; + +//======================================================================== +/* RX/TX both */ +//------------------------------------------------------------------------ +extern struct _uni { + REAL samplerate; + int buflen; + + struct { + SDRMODE sdr; + TRXMODE trx; + } mode; + + struct { + BOOLEAN flag; + struct { + char *path; + size_t size; + Chan c; + } chan; + REAL val, avgval; + METERTYPE type; + } meter; + + struct { + BOOLEAN flag; + PWS gen; + struct { + char *path; + size_t size; + Chan c; + } chan; + CXB buf; + int fftsize; + } powsp; + + struct { + BOOLEAN flag; + struct { + char *path; + size_t size; + Chan c; + } chan; + splitfld splt; + } update; + + struct { + char *path; + int bits; + } wisdom; + +} uni; + +//------------------------------------------------------------------------ +/* RX */ +//------------------------------------------------------------------------ +extern struct _rx { + struct { + CXB i, o; + } buf; + struct { + REAL freq, phase; + OSC gen; + } osc; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + COMPLEX * save; + } filt; + IQ iqfix; + struct { + REAL thresh; + NB gen; + BOOLEAN flag; + } nb; + struct { + REAL thresh; + NB gen; + BOOLEAN flag; + } nb_sdrom; + struct { + LMSR gen; + BOOLEAN flag; + } anr, anf; + struct { + DIGITALAGC gen; + BOOLEAN flag; + } agc; + struct { AMD gen; } am; + struct { FMD gen; } fm; + struct { + BOOLEAN flag; + SpotToneGen gen; + } spot; + struct { + struct { + REAL val; + BOOLEAN flag; + } pre, post; + } scl; + struct { + REAL thresh, power; + struct { + REAL meter, att, gain; + } offset; + BOOLEAN flag, running, set; + int num; + } squelch; + SDRMODE mode; + struct { BOOLEAN flag; } bin; +} rx; + +//------------------------------------------------------------------------ +/* TX */ +//------------------------------------------------------------------------ +extern struct _tx { + struct { + CXB i, o; + } buf; + struct { + REAL freq, phase; + OSC gen; + } osc; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + COMPLEX * save; + } filt; + struct { + ComplexFIR coef; + FiltOvSv ovsv; + CXB in, out; + } fm; + struct { + DIGITALAGC gen; + BOOLEAN flag; + } agc; + struct { + SpeechProc gen; + BOOLEAN flag; + } spr; + struct { + COMPLEX dc; + struct { + REAL val; + BOOLEAN flag; + } pre, post; + } scl; + SDRMODE mode; +} tx; + +//------------------------------------------------------------------------ + +typedef enum _runmode { + RUN_MUTE, RUN_PASS, RUN_PLAY, RUN_SWCH +} RUNMODE; + +#ifndef _WINDOWS + +extern struct _top { + pid_t pid; + uid_t uid; + + struct timeval start_tv; + + BOOLEAN running, verbose; + RUNMODE state; + + // audio io + struct { + struct { + float *l, *r; + } buf; + struct { + unsigned int frames, bytes; + } size; + } hold; + struct { + char *path; + int fd; + FILE * fp; + char buff[4096]; + } parm; + + struct { + char name[256]; + jack_client_t * client; + struct { + struct { + jack_port_t *l, *r; + } i, o; + } port; + struct { + struct { + jack_ringbuffer_t *l, *r; + } i, o; + } ring; + } jack; + + // update io + // multiprocessing & synchronization + struct { + struct { + pthread_t id; + } trx, upd, pws, meter; + } thrd; + struct { + struct { + sem_t sem; + } buf, upd; + } sync; + + // TRX switching + struct { + struct { + int want, have; + } bfct; + struct { + TRXMODE next; + } trx; + struct { + RUNMODE last; + } run; + int fade, tail; + } swch; +} top; + +#else + +extern struct _top { + pid_t pid; + uid_t uid; + + BOOLEAN running, verbose; + RUNMODE state; + + // audio io + struct { + struct { + float *l, *r; + } buf; + struct { + unsigned int frames, bytes; + } size; + } hold; + struct { + char *path; + int fd; + HANDLE *fpr; + HANDLE *fpw; + char buff[4096]; + } parm; + + struct { + char name[256]; + struct { + struct { + ringb_t *l, *r; + } i, o; + } ring; + } jack; + + // update io + // multiprocessing & synchronization + struct { + struct { + DWORD id; + HANDLE thrd; + } trx, upd, pws, meter; + } thrd; + struct { + struct { + HANDLE sem; + } buf, upd, upd2; + } sync; + + // TRX switching + struct { + struct { + int want, have; + } bfct; + struct { + TRXMODE next; + } trx; + struct { + RUNMODE last; + } run; + int fade, tail; + } swch; +} top; + +#endif diff --git a/jDttSP/win/update.c b/jDttSP/win/update.c new file mode 100644 index 0000000..baec77a --- /dev/null +++ b/jDttSP/win/update.c @@ -0,0 +1,1441 @@ +/* update.c + +common defs and code for parm update + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//////////////////////////////////////////////////////////////////////////// + +PRIVATE REAL +dB2lin(REAL dB) { return pow(10.0, dB / 20.0); } + + +PRIVATE int +setRXFilter(int n, char **p) { + REAL low_frequency = atof(p[0]), + high_frequency = atof(p[1]); + int ncoef = uni.buflen + 1; + int i, fftlen = 2 * uni.buflen; + fftw_plan ptmp; + COMPLEX *zcvec; + + if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; + if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; + if ((low_frequency + 10) >= high_frequency) return -3; + delFIR_COMPLEX(rx.filt.coef); + + rx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, FFTW_OUT_OF_PLACE); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = rx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = rx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) rx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + memcpy((char *) rx.filt.save, + (char *) rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + return 0; +} + + +PRIVATE int +setTXFilter(int n, char **p) { + REAL low_frequency = atof(p[0]), + high_frequency = atof(p[1]); + int ncoef = uni.buflen + 1; + int i, fftlen = 2 * uni.buflen; + fftw_plan ptmp; + COMPLEX *zcvec; + + if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; + if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; + if ((low_frequency + 10) >= high_frequency) return -3; + delFIR_COMPLEX(tx.filt.coef); + tx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, FFTW_OUT_OF_PLACE); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = tx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) tx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + memcpy((char *) tx.filt.save, + (char *) tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + return 0; +} + +PRIVATE int +setFilter(int n, char **p) { + if (n == 2) return setRXFilter(n, p); + else { + int trx = atoi(p[2]); + if (trx == RX) return setRXFilter(n, p); + else if (trx == TX) return setTXFilter(n, p); + else return -1; + } +} + +// setMode [TRX] + +PRIVATE int +setMode(int n, char **p) { + int mode = atoi(p[0]); + if (n > 1) { + int trx = atoi(p[1]); + switch (trx) { + case TX: tx.mode = mode; break; + case RX: + default: rx.mode = mode; break; + } + } else + tx.mode = rx.mode = uni.mode.sdr = mode; + if (rx.mode == AM) rx.am.gen->mode = AMdet; + if (rx.mode == SAM) rx.am.gen->mode = SAMdet; + return 0; +} + +// setOsc [TRX] +PRIVATE int +setOsc(int n, char **p) { + REAL newfreq = atof(p[0]); +// fprintf(stderr,"freq =%lf, samplerate = %lf\n",newfreq,uni.samplerate); + if (fabs(newfreq) >= 0.5 * uni.samplerate) return -1; + newfreq *= 2.0 * M_PI / uni.samplerate; + if (n > 1) { + int trx = atoi(p[1]); + switch (trx) { + case TX: tx.osc.gen->Frequency = newfreq; break; + case RX: + default: rx.osc.gen->Frequency = newfreq; break; + } + } else + tx.osc.gen->Frequency = rx.osc.gen->Frequency = newfreq; + return 0; +} + +PRIVATE int +setSampleRate(int n, char **p) { + REAL samplerate = atof(p[0]); + uni.samplerate = samplerate; + return 0; +} + +PRIVATE int +setNR(int n, char **p) { + rx.anr.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setANF(int n, char **p) { + rx.anf.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setNB(int n, char **p) { + rx.nb.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setNBvals(int n, char **p) { + REAL threshold = atof(p[0]); + rx.nb.gen->threshold = rx.nb.thresh = threshold; + return 0; +} + +PRIVATE int +setSDROM(int n, char **p) { + rx.nb_sdrom.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setSDROMvals(int n, char **p) { + REAL threshold = atof(p[0]); + rx.nb_sdrom.gen->threshold = rx.nb_sdrom.thresh = threshold; + return 0; +} + +PRIVATE int +setBIN(int n, char **p) { + rx.bin.flag = atoi(p[0]); + return 0; +} + +PRIVATE +// setfixedAGC [TRX] +int +setfixedAGC(int n, char **p) { + REAL gain = atof(p[0]); + if (n > 1) { + int trx = atoi(p[1]); + switch(trx) { + case TX: tx.agc.gen->AgcFixedGain = gain; break; + case RX: + default: rx.agc.gen->AgcFixedGain = gain; break; + } + } else + tx.agc.gen->AgcFixedGain = rx.agc.gen->AgcFixedGain = gain; + return 0; +} + +PRIVATE int +setTXAGC(int n, char **p) { + int setit = atoi(p[0]); + switch (setit) { + case agcOFF: + tx.agc.gen->AgcMode = agcOFF; + tx.agc.flag = FALSE; + break; + case agcSLOW: + tx.agc.gen->AgcMode = agcSLOW; + tx.agc.gen->Hang = 10; + tx.agc.flag = TRUE; + break; + case agcMED: + tx.agc.gen->AgcMode = agcMED; + tx.agc.gen->Hang = 6; + tx.agc.flag = TRUE; + break; + case agcFAST: + tx.agc.gen->AgcMode = agcFAST; + tx.agc.gen->Hang = 3; + tx.agc.flag = TRUE; + break; + case agcLONG: + tx.agc.gen->AgcMode = agcLONG; + tx.agc.gen->Hang = 23; + tx.agc.flag = TRUE; + break; + } + return 0; +} + +PRIVATE int +setTXAGCCompression(int n, char **p) { + REAL txcompression = atof(p[0]); + tx.agc.gen->MaxGain = pow(10.0 , txcompression * 0.05); + return 0; +} + +PRIVATE int +setTXAGCFF(int n, char **p) { + int setit = atoi(p[0]); + tx.spr.flag = setit; + return 0; +} + +PRIVATE int +setTXAGCFFCompression(int n, char **p) { + REAL txcompression = atof(p[0]); + tx.spr.gen->MaxGain = pow(10.0 , txcompression * 0.05); + return 0; +} + +PRIVATE int +setTXAGCHang(int n, char **p) { + int hang = atoi(p[0]); + tx.agc.gen->Hang = + (int) max(1, min(23, ((REAL) hang) * uni.samplerate / (1000.0 * uni.buflen))); + return 0; +} + +PRIVATE int +setTXAGCLimit(int n, char **p) { + REAL limit = atof(p[0]); + tx.agc.gen->AgcLimit = 0.001 * limit; + return 0; +} + +PRIVATE int +setTXSpeechCompression(int n, char **p) { + tx.spr.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXSpeechCompressionGain(int n, char **p) { + tx.spr.gen->MaxGain = dB2lin(atof(p[0])); + return 0; +} + +//============================================================ + +PRIVATE int +f2x(REAL f) { + REAL fix = tx.filt.ovsv->fftlen * f / uni.samplerate; + return (int) (fix + 0.5); +} + +//------------------------------------------------------------ + +PRIVATE void +apply_txeq_band(REAL lof, REAL dB, REAL hif) { + int i, + lox = f2x(lof), + hix = f2x(hif), + l = tx.filt.ovsv->fftlen; + REAL g = dB2lin(dB); + COMPLEX *src = tx.filt.save, + *trg = tx.filt.ovsv->zfvec; + for (i = lox; i < hix; i++) { + trg[i] = Cscl(src[i], g); + if (i) { + int j = l - i; + trg[j] = Cscl(src[j], g); + } + } +} + +// typical: +// 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600 +// approximates W2IHY bandcenters. +// no args, revert to no EQ. +PRIVATE int +setTXEQ(int n, char **p) { + if (n < 3) { + // revert to no EQ + memcpy((char *) tx.filt.ovsv->zfvec, + (char *) tx.filt.save, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + return 0; + } else { + int i; + REAL lof = atof(p[0]); + for (i = 0; i < n - 2; i += 2) { + REAL dB = atof(p[i + 1]), + hif = atof(p[i + 2]); + if (lof < 0.0 || hif <= lof) return -1; + apply_txeq_band(lof, dB, hif); + lof = hif; + } + return 0; + } +} + +//------------------------------------------------------------ + +PRIVATE void +apply_rxeq_band(REAL lof, REAL dB, REAL hif) { + int i, + lox = f2x(lof), + hix = f2x(hif), + l = rx.filt.ovsv->fftlen; + REAL g = dB2lin(dB); + COMPLEX *src = rx.filt.save, + *trg = rx.filt.ovsv->zfvec; + for (i = lox; i < hix; i++) { + trg[i] = Cscl(src[i], g); + if (i) { + int j = l - i; + trg[j] = Cscl(src[j], g); + } + } +} + +PRIVATE int +setRXEQ(int n, char **p) { + if (n < 3) { + // revert to no EQ + memcpy((char *) rx.filt.ovsv->zfvec, + (char *) rx.filt.save, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + return 0; + } else { + int i; + REAL lof = atof(p[0]); + for (i = 0; i < n - 2; i += 2) { + REAL dB = atof(p[i + 1]), + hif = atof(p[i + 2]); + if (lof < 0.0 || hif <= lof) return -1; + apply_rxeq_band(lof, dB, hif); + lof = hif; + } + return 0; + } +} + +//============================================================ +PRIVATE int +setRXAGC(int n, char **p) { + int setit = atoi(p[0]); + switch (setit) { + case agcOFF: + rx.agc.gen->AgcMode = agcOFF; + rx.agc.flag = TRUE; + break; + case agcSLOW: + rx.agc.gen->AgcMode = agcSLOW; + rx.agc.gen->Hang = 10; + rx.agc.flag = TRUE; + break; + case agcMED: + rx.agc.gen->AgcMode = agcMED; + rx.agc.gen->Hang = 6; + rx.agc.flag = TRUE; + break; + case agcFAST: + rx.agc.gen->AgcMode = agcFAST; + rx.agc.gen->Hang = 3; + rx.agc.flag = TRUE; + break; + case agcLONG: + rx.agc.gen->AgcMode = agcLONG; + rx.agc.gen->Hang = 23; + rx.agc.flag = TRUE; + break; + } + return 0; +} + +PRIVATE int +setANFvals(int n, char **p) { + int taps = atoi(p[0]), + delay = atoi(p[1]); + REAL gain = atof(p[2]), + leak = atof(p[3]); + rx.anf.gen->adaptive_filter_size = taps; + rx.anf.gen->delay = delay; + rx.anf.gen->adaptation_rate = gain; + rx.anf.gen->leakage = leak; + return 0; +} + +PRIVATE int +setNRvals(int n, char **p) { + int taps = atoi(p[0]), + delay = atoi(p[1]); + REAL gain = atof(p[2]), + leak = atof(p[3]); + rx.anr.gen->adaptive_filter_size = taps; + rx.anr.gen->delay = delay; + rx.anr.gen->adaptation_rate = gain; + rx.anr.gen->leakage = leak; + return 0; +} + +PRIVATE int +setcorrectIQ(int n, char **p) { + int phaseadjustment = atoi(p[0]), + gainadjustment = atoi(p[1]); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + rx.iqfix->gain = 1.0+ 0.001 * (REAL) gainadjustment; + return 0; +} + +PRIVATE int +setcorrectIQphase(int n, char **p) { + int phaseadjustment = atoi(p[0]); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + return 0; +} + +PRIVATE int +setcorrectIQgain(int n, char **p) { + int gainadjustment = atoi(p[0]); + rx.iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment; + return 0; +} + +PRIVATE int +setSquelch(int n, char **p) { + rx.squelch.thresh = -atof(p[0]); + return 0; +} + +PRIVATE int +setMeterOffset(int n, char **p) { + rx.squelch.offset.meter = atof(p[0]); + return 0; +} + +PRIVATE int +setATTOffset(int n, char **p) { + rx.squelch.offset.att = atof(p[0]); + return 0; +} + +PRIVATE int +setGainOffset(int n, char **p) { + rx.squelch.offset.gain = atof(p[0]); + return 0; +} + +PRIVATE int +setSquelchSt(int n, char **p) { + rx.squelch.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTRX(int n, char **p) { + uni.mode.trx = atoi(p[0]); + return 0; +} + +PRIVATE int +setRunState(int n, char **p) { + RUNMODE rs = (RUNMODE) atoi(p[0]); + top.state = rs; + return 0; +} + +PRIVATE int +setSpotToneVals(int n, char **p) { + REAL gain = atof(p[0]), + freq = atof(p[1]), + rise = atof(p[2]), + fall = atof(p[3]); + setSpotToneGenVals(rx.spot.gen, gain, freq, rise, fall); + return 0; +} + +PRIVATE int +setSpotTone(int n, char **p) { + if (atoi(p[0])) { + SpotToneOn(rx.spot.gen); + rx.spot.flag = TRUE; + } else + SpotToneOff(rx.spot.gen); + return 0; +} + +PRIVATE int +setRXPreScl(int n, char **p) { + rx.scl.pre.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setRXPreSclVal(int n, char **p) { + rx.scl.pre.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setTXPreScl(int n, char **p) { + tx.scl.pre.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXPreSclVal(int n, char **p) { + tx.scl.pre.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setRXPostScl(int n, char **p) { + rx.scl.post.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setRXPostSclVal(int n, char **p) { + rx.scl.post.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setTXPostScl(int n, char **p) { + tx.scl.post.flag = atoi(p[0]); + return 0; +} + +PRIVATE int +setTXPostSclVal(int n, char **p) { + tx.scl.post.val = dB2lin(atof(p[0])); + return 0; +} + +PRIVATE int +setFinished(int n, char **p) { + top.running = FALSE; + return 0; +} + +PRIVATE int +setPWSmode(int n,char **p) { + int setit = atoi(p[0]); + uni.powerspectrum.pws->Mode = setit; + return 0; +} + +PRIVATE int +setPWSSubmode(int n,char **p) { + int setit = atoi(p[0]); + uni.powerspectrum.pws->SubMode = setit; + return 0; +} + +// next-trx-mode [nbufs-to-zap] +PRIVATE int +setSWCH(int n, char **p) { + top.swch.trx.next = atoi(p[0]); + if (n > 1) top.swch.bfct.want = atoi(p[1]); + else top.swch.bfct.want = 0; + top.swch.bfct.have = 0; + top.swch.run.last = top.state; + top.state = RUN_SWCH; + return 0; +} + +//======================================================================== + +#include + +CTE update_cmds[] = { + {"setANF", setANF}, + {"setANFvals", setANFvals}, + {"setATTOffset", setATTOffset}, + {"setBIN", setBIN}, + {"setcorrectIQ", setcorrectIQ}, + {"setcorrectIQgain", setcorrectIQgain}, + {"setcorrectIQphase", setcorrectIQphase}, + {"setFilter", setFilter}, + {"setfixedAGC", setfixedAGC}, + {"setGainOffset", setGainOffset}, + {"setMeterOffset", setMeterOffset}, + {"setMode", setMode}, + {"setNB", setNB}, + {"setNBvals", setNBvals}, + {"setNR", setNR}, + {"setNRvals", setNRvals}, + {"setOsc", setOsc}, + {"setRXAGC", setRXAGC}, + {"setRunState", setRunState}, + {"setSampleRate", setSampleRate}, + {"setSquelch", setSquelch}, + {"setSquelchSt", setSquelchSt}, + {"setTXAGC", setTXAGC}, + {"setTXAGCCompression", setTXAGCCompression}, + {"setTXAGCFFCompression",setTXAGCFFCompression}, + {"setTXAGCHang", setTXAGCHang}, + {"setTXAGCLimit", setTXAGCLimit}, + {"setTXSpeechCompression", setTXSpeechCompression}, + {"setTXSpeechCompressionGain", setTXSpeechCompressionGain}, + {"setRXEQ", setRXEQ}, + {"setTXEQ", setTXEQ}, + {"setTRX", setTRX}, + {"setRXPreScl", setRXPreScl}, + {"setRXPreSclVal", setRXPreSclVal}, + {"setTXPreScl", setTXPreScl}, + {"setTXPreSclVal", setTXPreSclVal}, + {"setRXPostScl", setRXPostScl}, + {"setRXPostSclVal", setRXPostSclVal}, + {"setTXPostScl", setTXPostScl}, + {"setTXPostSclVal", setTXPostSclVal}, + {"setFinished", setFinished}, + {"setSWCH", setSWCH}, + {"setSpotToneVals", setSpotToneVals}, + {"setSpotTone", setSpotTone}, + {"setPWSmode", setPWSmode}, + {"setPWSSubmode",setPWSSubmode}, + {"setSDROM", setSDROM}, + {"setSDROMvals",setSDROMvals}, + { 0, 0 } +}; + +//........................................................................ + +int do_update(char *str) { + + SPLIT splt = &uni.update.splt; + + split(splt, str); + + if (NF(splt) < 1) return -1; + else { + Thunk thk = Thunk_lookup(update_cmds, F(splt, 0)); + if (!thk) { + fprintf(stderr,"-1\n"); + return -1; + } else { + int val; + WaitForSingleObject(top.sync.upd.sem,INFINITE); + val = (*thk)(NF(splt) - 1, Fptr(splt, 1)); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + return val; + } + } +} + +BOOLEAN newupdates() { + WaitForSingleObject(top.sync.upd2.sem,INFINITE); + return TRUE; +} + +PRIVATE void +sendcommand(char *str) { + putChan_nowait(uni.update.chan.c,str,strlen(str)+1);; + ReleaseSemaphore(top.sync.upd2.sem,1L,NULL); + Sleep(0); +} + +#ifdef PROCESSVERS +DttSP_EXP int +changeMode(SDRMODE mode) { + char str[128]; + switch(mode) { + case LSB: + sprintf(str,"setMode %d",LSB); + sendcommand(str); + break; + case USB: + sprintf(str,"setMode %d",USB); + sendcommand(str); + break; + case DSB: + sprintf(str,"setMode %d",DSB); + sendcommand(str); + break; + case CWL: + sprintf(str,"setMode %d",CWL); + sendcommand(str); + break; + case CWU: + sprintf(str,"setMode %d",CWU); + sendcommand(str); + break; + case AM: + sprintf(str,"setMode %d",AM); + sendcommand(str); + break; + case PSK: + sprintf(str,"setMode %d",PSK); + sendcommand(str); + break; + case SPEC: + sprintf(str,"setMode %d",SPEC); + sendcommand(str); + break; + case RTTY: + sprintf(str,"setMode %d",RTTY); + sendcommand(str); + break; + case SAM: + sprintf(str,"setMode %d",SAM); + sendcommand(str); + break; + case DRM: + sprintf(str,"setMode %d",DRM); + sendcommand(str); + break; + default: + break; + } + return 0; +} + +DttSP_EXP int +changeOsc(REAL newfreq){ + char str[64]; + if (fabs(newfreq) >= 0.5* uni.samplerate) return -1; + sprintf(str,"setOsc %lf RX",newfreq); + sendcommand(str); + return 0; +} + +DttSP_EXP int +changeTXOsc(REAL newfreq){ + char str[64]; + if (fabs(newfreq) >= 0.5* uni.samplerate) return -1; + sprintf(str,"setOsc %lf TX",newfreq); + sendcommand(str); + return 0; +} + +DttSP_EXP int +changeSampleRate(REAL newSampleRate) { + char str[64]; + sprintf(str,"setSampleRate %9.0lf",newSampleRate); + sendcommand(str); + return 0; +} + +DttSP_EXP void changeNR(int setit) { + char str[64]; + sprintf(str,"setNR %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeANF(int setit) { + char str[64]; + sprintf(str,"setANF %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeNB(int setit) { + char str[64]; + sprintf(str,"setNB %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeNBvals(REAL threshold) { + char str[64]; + sprintf(str,"setNBvals %lf",threshold); + sendcommand(str); +} + + +DttSP_EXP void changeBIN(int setit) { + char str[64]; + sprintf(str,"setBIN %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeAGC(AGCMODE setit) { + char str[64]; + switch (setit) { + case agcOFF: + sprintf(str,"setRXAGC %d",agcOFF); + break; + case agcLONG: + sprintf(str,"setRXAGC %d",agcLONG); + break; + case agcSLOW: + sprintf(str,"setRXAGC %d",agcSLOW); + break; + case agcMED: + sprintf(str,"setRXAGC %d",agcMED); + break; + case agcFAST: + sprintf(str,"setRXAGC %d",agcFAST); + break; + } + sendcommand(str); +} + +DttSP_EXP void changeANFvals(int taps, int delay, REAL gain, REAL leak) { + char str[128]; + sprintf(str,"setANFvals %3d %3d %9.7lf %9.7lf",taps,delay,gain,leak); + sendcommand(str); +} + +DttSP_EXP void changeNRvals(int taps, int delay, REAL gain, REAL leak) { + char str[128]; + sprintf(str,"setNRvals %3d %3d %9.7lf %9.7lf",taps,delay,gain,leak); + sendcommand(str); +} + +DttSP_EXP void setcorrectIQcmd(int phaseadjustment,int gainadjustment) { + char str[64]; + sprintf(str,"setcorrectIQ %d %d",phaseadjustment,gainadjustment); + sendcommand(str); +} + +DttSP_EXP void changecorrectIQphase(int phaseadjustment){ + char str[64]; + sprintf(str,"setcorrectIQphase %d",phaseadjustment); + sendcommand(str); +} + +DttSP_EXP void changecorrectIQgain(int gainadjustment){ + char str[64]; + sprintf(str,"setcorrectIQgain %d",gainadjustment); + sendcommand(str); +} + +DttSP_EXP int +changeFilter(REAL low_frequency,REAL high_frequency,int ncoef, TRXMODE trx) { + char str[64]; + if (trx == RX) sprintf(str,"setFilter %6.0lf %6.0lf RX",low_frequency,high_frequency); + else sprintf(str,"setFilter %6.0lf %6.0lf TX",low_frequency,high_frequency); + sendcommand(str); + return 0; +} + +DttSP_EXP void +changePWSmode(PWSMODE setit) { + char str[64]; + switch (setit) { + case PREFILTER: + sprintf(str,"setPWSmode %d",PREFILTER); + break; + case POSTFILTER: + sprintf(str,"setPWSmode %d",POSTFILTER); + break; + case AUDIO: + sprintf(str,"setPWSmode %d",AUDIO); + break; + default: + return; + } + sendcommand(str); +} + +DttSP_EXP void +changePWSsubmode(PWSSUBMODE setit) { + char str[64]; + switch (setit) { + case SPECTRUM: + sprintf(str,"setPWSSubmode %d",SPECTRUM); + break; + case PHASE: + sprintf(str,"setPWSSubmode %d",PHASE); + break; + case SCOPE: + sprintf(str,"setPWSSubmode %d",SCOPE); + break; + case PHASE2: + sprintf(str,"setPWSSubmode %d",PHASE); + break; + case WATERFALL: + sprintf(str,"setPWSSubmode %d",WATERFALL); + break; + case HISTOGRAM: + sprintf(str,"setPWSSubmode %d",HISTOGRAM); + break; + default: + return; + } + sendcommand(str); +} + +DttSP_EXP void +changeWindow(Windowtype Windowset){ +} + +DttSP_EXP void +oldsetTXEQ(int *txeq) { + char str[256]; + sprintf(str, + "setTXEQ 0 %d 450 %d 800 %d 1150 %d 1450 %d 1800 %d 2150 %d 2450 %d 2800 %d 3600", + txeq[0], + txeq[1], + txeq[2], + txeq[3], + txeq[4], + txeq[5], + txeq[6], + txeq[7], + txeq[8]); + sendcommand(str); +} + +DttSP_EXP void +changeTXAGCCompression(double txc) { + char str[64]; + sprintf(str,"setTXAGCCompression %lf",txc); + sendcommand(str); +} + +DttSP_EXP void +changeTXAGCFFCompression(double txc) { + char str[64]; + sprintf(str,"setTXAGCFFCompression %lf",txc); + sendcommand(str); +} + +DttSP_EXP void oldsetGainOffset(float val) { + char str[64]; + sprintf(str,"setGainOffset %f",val); + sendcommand(str); +} + +DttSP_EXP void setTXScale(REAL scale) { + char str[32]; + sprintf(str,"setTXPreScl 1 "); + sendcommand(str); + Sleep(0); + sprintf(str,"setTXPreSclVal %lf",20.0*log10(scale)); + sendcommand(str); +} + +DttSP_EXP void oldsetATTOffset(float val){ + char str[64]; + sprintf(str,"setATTOffset %f",val); + sendcommand(str); +} + +DttSP_EXP setRXScale(float val) { + char str[64]; + sprintf(str,"setRXPostScl 1"); + sendcommand(str); + Sleep(0); + sprintf(str,"setRXPostSclVal %f",val); + sendcommand(str); +} + +DttSP_EXP oldsetMeterOffset(float val){ + char str[64]; + sprintf(str,"setMeterOffset %f",val); + sendcommand(str); +} + +DttSP_EXP changeSquelch(float setit) { + char str[64]; + sprintf(str,"setSquelch %f",setit); + sendcommand(str); +} + +DttSP_EXP changeSquelchSt(BOOLEAN setit) { + char str[64]; + sprintf(str,"setSquelch %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeSDROM(BOOLEAN setit) { + char str[64]; + sprintf(str,"setSDROM %d",setit); + sendcommand(str); +} + +DttSP_EXP void changeSDROMvals(REAL threshold) { + char str[64]; + sprintf(str,"setSDROMvals %lf",threshold); + sendcommand(str); +} + + +#endif // END PROCESSVERS + + + +#ifdef DLLVERS +DttSP_EXP int +changeMode(SDRMODE mode) { + switch(mode) { + case LSB: + case USB: + case DSB: + case CWL: + case CWU: + case FMN: + case AM: + case PSK: + case SPEC: + case RTTY: + case SAM: + case DRM: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.mode = tx.mode = uni.mode.sdr = mode; + if (mode == SAM) rx.am.gen->mode = SAMdet; + if (mode == AM) rx.am.gen->mode = AMdet; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + default: + break; + } + return 0; +} + +DttSP_EXP int +changeOsc(REAL newfreq){ + if (fabs(newfreq) >= 0.5* uni.samplerate) return -1; + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.osc.gen->Frequency = rx.osc.freq = 2.0*M_PI*newfreq/uni.samplerate; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + return 0; +} + +DttSP_EXP int +changeTXOsc(REAL newfreq){ + if (fabs(newfreq) >= 0.5* uni.samplerate) return -1; + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.osc.gen->Frequency = tx.osc.freq = 2.0*M_PI*newfreq/uni.samplerate; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + return 0; +} + +DttSP_EXP int +changeSampleRate(REAL newSampleRate) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + uni.samplerate = newSampleRate; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + return 0; +} + +DttSP_EXP void changeNR(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.anr.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeANF(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.anf.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeNB(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.nb.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeNBvals(REAL threshold) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.nb.gen->threshold = rx.nb.thresh = threshold; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeSDROM(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.nb_sdrom.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeSDROMvals(REAL threshold) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.nb_sdrom.gen->threshold = rx.nb_sdrom.thresh = threshold; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + + +DttSP_EXP void changeBIN(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.bin.flag= setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeAGC(AGCMODE setit) { + switch (setit) { + case agcOFF: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcMode = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + case agcLONG: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcMode = setit; + rx.agc.gen->Hang = 23; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + case agcSLOW: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcMode = setit; + rx.agc.gen->Hang = 7; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + case agcMED: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcMode = setit; + rx.agc.gen->Hang = 4; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + case agcFAST: + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcMode = setit; + rx.agc.gen->Hang = 2; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + break; + default: + break; + } +} + +DttSP_EXP void changefixedAGC(REAL fixed_agc) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcFixedGain = dB2lin(fixed_agc); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changemaxAGC(REAL max_agc) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->MaxGain = dB2lin(max_agc); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeRXAGCAttackTimeScale(int limit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcAttackTimeScale = limit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + +} + +DttSP_EXP void changeRXAGCHang(int limit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->Hang = limit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + +} + +DttSP_EXP void changeRXAGCLimit(int limit){ + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.agc.gen->AgcLimit = 0.001 * (double)limit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeANFvals(int taps, int delay, REAL gain, REAL leak) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.anf.gen->adaptation_rate = gain; + rx.anf.gen->adaptive_filter_size = taps; + rx.anf.gen->delay = delay; + rx.anf.gen->leakage = leak; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void changeNRvals(int taps, int delay, REAL gain, REAL leak) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.anr.gen->adaptation_rate = gain; + rx.anr.gen->adaptive_filter_size = taps; + rx.anr.gen->delay = delay; + rx.anr.gen->leakage = leak; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +setcorrectIQcmd(int phaseadjustment,int gainadjustment) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + rx.iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changecorrectIQphase(int phaseadjustment) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changecorrectIQgain(int gainadjustment) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.iqfix->gaina = 1.0 + 0.001 * (REAL) gainadjustment; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP int +changeFilter(REAL low_frequency,REAL high_frequency,int ncoef, TRXMODE trx) { + int i, fftlen = 2 * uni.buflen; + fftw_plan ptmp; + COMPLEX *zcvec; + ncoef = uni.buflen + 1; + + if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; + if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; + if ((low_frequency + 10) >= high_frequency) return -3; + WaitForSingleObject(top.sync.upd.sem,INFINITE); + if (trx == RX) { + delFIR_COMPLEX(rx.filt.coef); + + rx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef + 1); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, FFTW_OUT_OF_PLACE); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = rx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = rx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) rx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + memcpy((char *) rx.filt.save, + (char *) rx.filt.ovsv->zfvec, + rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + } else { + delFIR_COMPLEX(tx.filt.coef); + + tx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef + 1); + + zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); + ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, FFTW_OUT_OF_PLACE); +#ifdef LHS + for (i = 0; i < ncoef; i++) zcvec[i] = tx.filt.coef->coef[i]; +#else + for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i]; +#endif + fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) tx.filt.ovsv->zfvec); + fftw_destroy_plan(ptmp); + delvec_COMPLEX(zcvec); + memcpy((char *) tx.filt.save, + (char *) tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + } + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + return 0; +} + +DttSP_EXP void +changePWSmode(PWSMODE setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + uni.powerspectrum.pws->Mode = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + +} + +DttSP_EXP void +changePWSsubmode(PWSSUBMODE setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + uni.powerspectrum.pws->SubMode = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeWindow(Windowtype Windowset){ +} + +DttSP_EXP void +oldsetTXEQ(int *txeq) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + apply_txeq_band(0.0 ,txeq[ 0],120.0); + apply_txeq_band(120.0 ,txeq[ 1],230.0); + apply_txeq_band(230.0 ,txeq[ 2],450.0); + apply_txeq_band(450.0 ,txeq[ 3],800.0); + apply_txeq_band(800.0 ,txeq[ 4],1150.0); + apply_txeq_band(1150.0,txeq[ 5],1450.0); + apply_txeq_band(1450.0,txeq[ 6],1800.0); + apply_txeq_band(1800.0,txeq[ 7],2150.0); + apply_txeq_band(2150.0,txeq[ 8],2450.0); + apply_txeq_band(2450.0,txeq[ 9],2800.0); + apply_txeq_band(2800.0,txeq[10],3250.0); + apply_txeq_band(3250.0,txeq[11],6000.0); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeTXAGCCompression(double txc) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.agc.gen->MaxGain = pow(10.0 , txc * 0.05); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeTXAGCFF(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.spr.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeTXAGCLimit(int setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.agc.gen->AgcLimit = 0.001*(REAL)setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeTXAGCHang(int hang) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.agc.gen->Hang = (int)max(1,min(23,((REAL)hang) * uni.samplerate /(1000.0*uni.buflen))); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void +changeTXAGCFFCompression(REAL txc) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.spr.gen->MaxGain = pow(10.0 , txc * 0.05); + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void setTXScale(REAL scale) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + tx.scl.post.flag = TRUE; + tx.scl.post.val = scale; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void oldsetGainOffset(float val) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.squelch.offset.gain = (REAL)val; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP void oldsetATTOffset(float val){ + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.squelch.offset.att = (REAL)val; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP oldsetMeterOffset(float val){ + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.squelch.offset.meter = (REAL)val; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + + +DttSP_EXP setRXScale(REAL val) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.scl.post.flag = TRUE; + rx.scl.post.val = val; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP changeSquelch(int setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.squelch.thresh = -(REAL)setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} + +DttSP_EXP changeSquelchSt(BOOLEAN setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + rx.squelch.flag = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); + +} + +DttSP_EXP void oldsetTRX(TRXMODE setit) { + WaitForSingleObject(top.sync.upd.sem,INFINITE); + /* top.swch.trx.next = setit; + top.swch.bfct.want = 2; + top.swch.bfct.have = 0; + top.swch.run.last = top.state; + top.state = RUN_SWCH; */ + uni.mode.trx = setit; + ReleaseSemaphore(top.sync.upd.sem,1L,NULL); +} +#endif // END DLLVERS + + + diff --git a/jDttSP/win/update.h b/jDttSP/win/update.h new file mode 100644 index 0000000..ea56405 --- /dev/null +++ b/jDttSP/win/update.h @@ -0,0 +1,46 @@ +/* update.h + +common defs and code for parm update + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _update_h +#define _update_h + +#include +#include +#include + +extern int do_update(char *str); +extern BOOLEAN newupdates(void); + +#endif diff --git a/jDttSP/win/valueswin.h b/jDttSP/win/valueswin.h new file mode 100644 index 0000000..1fa0230 --- /dev/null +++ b/jDttSP/win/valueswin.h @@ -0,0 +1,83 @@ +/* Old compatibility names for and constants. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This interface is obsolete. New programs should use + and/or instead of . */ + +#ifndef _VALUES_H +#define _VALUES_H 1 + +//#include + +#include + +#define _TYPEBITS(type) (sizeof (type) * CHAR_BIT) + +#define CHARBITS _TYPEBITS (char) +#define SHORTBITS _TYPEBITS (short int) +#define INTBITS _TYPEBITS (int) +#define LONGBITS _TYPEBITS (long int) +#define PTRBITS _TYPEBITS (char *) +#define DOUBLEBITS _TYPEBITS (double) +#define FLOATBITS _TYPEBITS (float) + +//#ifndef MINSHORT +//#define MINSHORT SHRT_MIN +//#endif + +#ifndef MININT +#define MININT INT_MIN +#endif + +//#ifndef MINLONG +//#define MINLONG LONG_MIN +//#endif +// +//#ifndef MAXSHORT +//#define MAXSHORT SHRT_MAX +//#endif + +#define MAXINT INT_MAX + +//#ifndef MAXLONG +//#define MAXLONG LONG_MAX +//#endif + +#define HIBITS MINSHORT +#define HIBITL MINLONG + + +#include + +#define MAXDOUBLE DBL_MAX +#define MAXFLOAT FLT_MAX +#define MINDOUBLE DBL_MIN +#define MINFLOAT FLT_MIN +#define DMINEXP DBL_MIN_EXP +#define FMINEXP FLT_MIN_EXP +#define DMAXEXP DBL_MAX_EXP +#define FMAXEXP FLT_MAX_EXP + + +#ifdef __USE_MISC +/* Some systems define this name instead of CHAR_BIT or CHARBITS. */ +# define BITSPERBYTE CHAR_BIT +#endif + +#endif /* values.h */ diff --git a/jDttSP/window.c b/jDttSP/window.c new file mode 100644 index 0000000..9807bfe --- /dev/null +++ b/jDttSP/window.c @@ -0,0 +1,158 @@ +/* window.c +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY +Implemented from code by Bill Schottstaedt of Snd Editor at CCRMA + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +/* shamelessly stolen from Bill Schottstaedt's clm.c */ +/* made worse in the process, but enough for our purposes here */ + +//static double +//sqr(double x) { return (x * x); } + +/* mostly taken from + * Fredric J. Harris, "On the Use of Windows for Harmonic Analysis with the + * Discrete Fourier Transform," Proceedings of the IEEE, Vol. 66, No. 1, + * January 1978. + * Albert H. Nuttall, "Some Windows with Very Good Sidelobe Behaviour", + * IEEE Transactions of Acoustics, Speech, and Signal Processing, Vol. ASSP-29, + * No. 1, February 1981, pp 84-91 + * + * JOS had slightly different numbers for the Blackman-Harris windows. + */ + +double * +makewindow(Windowtype type, int size, double *window) { + int i, j, midn, midp1, midm1; + double freq, rate, sr1, angle, expn, expsum, cx, two_pi; + + midn = size >> 1; + midp1 = (size + 1) / 2; + midm1 = (size - 1) / 2; + two_pi = 8.0 * atan(1.0); + freq = two_pi / (double) size; + rate = 1.0 / (double) midn; + angle = 0.0; + expn = log(2.0) / (double) midn + 1.0; + expsum = 1.0; + + switch (type) { + case RECTANGULAR_WINDOW: + for (i = 0; i < size; i++) window[i] = 1.0; + break; + case HANNING_WINDOW: /* Hann would be more accurate */ + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += freq) + window[j] = (window[i] = 0.5 - 0.5 * cos(angle)); + break; + case WELCH_WINDOW: + for (i = 0, j = size - 1; i <= midn; i++, j--) + window[j] = + (window[i] = 1.0 - sqr((double) (i - midm1) / (double) midp1)); + break; + case PARZEN_WINDOW: + for (i = 0, j = size - 1; i <= midn; i++, j--) + window[j] = + (window[i] = 1.0 - fabs((double) (i - midm1) / (double) midp1)); + break; + case BARTLETT_WINDOW: + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += rate) + window[j] = (window[i] = angle); + break; + case HAMMING_WINDOW: + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += freq) + window[j] = (window[i] = 0.54 - 0.46 * cos(angle)); + break; + case BLACKMAN2_WINDOW: /* using Chebyshev polynomial equivalents here */ + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += freq) { + cx = cos(angle); + window[j] = (window[i] = (.34401 + (cx * (-.49755 + (cx * .15844))))); + } + break; + case BLACKMAN3_WINDOW: + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += freq) { + cx = cos(angle); + window[j] = + (window[i] = (.21747 + (cx * (-.45325 + (cx * (.28256 - (cx * .04672))))))); + } + break; + case BLACKMAN4_WINDOW: + for (i = 0, j = size - 1, angle = 0.0; i <= midn; i++, j--, angle += freq) { + cx = cos(angle); + window[j] = (window[i] = + (.084037 + + (cx * + (-.29145 + + (cx * + (.375696 + (cx * (-.20762 + (cx * .041194))))))))); + } + break; + case EXPONENTIAL_WINDOW: + for (i = 0, j = size - 1; i <= midn; i++, j--) { + window[j] = (window[i] = expsum - 1.0); + expsum *= expn; + } + break; + case RIEMANN_WINDOW: + sr1 = two_pi / (double) size; + for (i = 0, j = size - 1; i <= midn; i++, j--) { + if (i == midn) window[j] = (window[i] = 1.0); + else { + /* split out because NeXT C compiler can't handle the full expression */ + cx = sr1 * (midn - i); + window[i] = sin(cx) / cx; + window[j] = window[i]; + } + } + break; + case BLACKMANHARRIS_WINDOW: + { + double + a0 = 0.35875, + a1 = 0.48829, + a2 = 0.14128, + a3 = 0.01168; + + + for (i = 0; i +#include +#include +#include +#include + +/* #define RECTANGULAR_WINDOW 1 +#define HANNING_WINDOW 2 +#define WELCH_WINDOW 3 +#define PARZEN_WINDOW 4 +#define BARTLETT_WINDOW 5 +#define HAMMING_WINDOW 6 +#define BLACKMAN2_WINDOW 7 +#define BLACKMAN3_WINDOW 8 +#define BLACKMAN4_WINDOW 9 +#define EXPONENTIAL_WINDOW 10 +#define RIEMANN_WINDOW 11 */ + +typedef enum _windowtype { + RECTANGULAR_WINDOW, + HANNING_WINDOW, + WELCH_WINDOW, + PARZEN_WINDOW, + BARTLETT_WINDOW, + HAMMING_WINDOW, + BLACKMAN2_WINDOW, + BLACKMAN3_WINDOW, + BLACKMAN4_WINDOW, + EXPONENTIAL_WINDOW, + RIEMANN_WINDOW, + BLACKMANHARRIS_WINDOW, +} Windowtype; + +extern double *makewindow(Windowtype type, int size, double *window); +//extern char *window_name(int n); +extern double sqr(double x); + +#endif diff --git a/pyhw/Makefile b/pyhw/Makefile new file mode 100644 index 0000000..d97d017 --- /dev/null +++ b/pyhw/Makefile @@ -0,0 +1,8 @@ +python: + swig -python hardware.i + gcc -fPIC -I. -I/usr/include/python -c hardware_wrap.c + gcc -fPIC -I. -c hardware.c + ld -shared hardware.o hardware_wrap.o -o _sdr1khw.so +clean: + /bin/rm -f hardware_wrap.c sdr1khw.py *.o *.so *.pyc + diff --git a/pyhw/README b/pyhw/README new file mode 100644 index 0000000..28307a7 --- /dev/null +++ b/pyhw/README @@ -0,0 +1,37 @@ +======================================================================== +Simple stuff. + +To use, run python interactively. Then + +>>> execfile("sdr1k-setup.py") + +to get the interface started. + +To set the frequency, do + +>>> setDDSFreq(14.077) + +To toggle the mute relay, + +>>> setMuteRelay(not getMuteRelay()) + +etc. + +To shut down, + +>>> closeRig() + +and exit python. (The startup script, which is crude and will be +replaced shortly, does an openRig("/dev/parport0") +======================================================================== + +To build: + +There is a simple Makefile that does just the following -- + +swig -python hardware.i +gcc -fPIC -I. -I/usr/include/python -c hardware_wrap.c +gcc -fPIC -I. -c hardware.c +ld -shared hardware.o hardware_wrap.o -o _sdr1khw.so + +Ready to go. diff --git a/pyhw/hardware.c b/pyhw/hardware.c new file mode 100644 index 0000000..65d8477 --- /dev/null +++ b/pyhw/hardware.c @@ -0,0 +1,985 @@ +/* hardware.c */ + +#include + +Rig myRig; + +PRIVATE void CalcClock(void); +PRIVATE void Latch(CtrlPin vCtrlPin); +PRIVATE void PWrite(BYTE data); +PRIVATE void DDSWrite(BYTE data, BYTE addr); +PRIVATE void SetRadioFreq(double f); +PRIVATE void Delay(void); +PRIVATE void ResetLatches(void); +PRIVATE void ResetDDS(void); +BOOLEAN InputPin(StatusPin vStatusPin); + +static int counter = 0; + +// ====================================================== +// Properties +// ====================================================== + +BOOLEAN +getExtended(void) { + return myRig.extended; +} + +void +setExtended(BOOLEAN value) { + myRig.extended = value; +} + +BOOLEAN +getRFE_Enabled(void) { + return myRig.rfe_enabled; +} + +void +setRFE_Enabled(BOOLEAN value) { + myRig.rfe_enabled = value; +} + +BOOLEAN +getXVTR_Enabled(void) { + return myRig.xvtr_enabled; +} + +void +setXVTR_Enabled(BOOLEAN value) { + myRig.xvtr_enabled = value; + if (myRig.xvtr_enabled) + myRig.max_freq = 146.0; + else + myRig.max_freq = 65.0; +} + +// private BOOLEANxvtr_tr_logic = FALSE; +BOOLEAN +getXVTR_TR_Logic(void) { + return myRig.xvtr_tr_logic; +} + +void +setXVTR_TR_Logic(BOOLEAN value) { + myRig.xvtr_tr_logic = value; +} + +int +getLatchDelay(void) { + return myRig.latch_delay; +} + +void +setLatchDelay(int value) { + myRig.latch_delay = value; +} + +double +getMinFreq(void) { + return myRig.min_freq; +} + +double +getMaxFreq(void) { + return myRig.max_freq; +} + +/* unsigned short +getBaseAddr(void) { + return myRig.baseAdr; +} + +void +setBaseAddr(unsigned short value) { + myRig.baseAdr = value; + }*/ + +BandSetting +getBandRelay(void) { + return myRig.band_relay; +} + +void +setBandRelay(BandSetting value) { + + extern void Pwrite(unsigned char); + myRig.band_relay = value; + PWrite((unsigned char) (myRig.band_relay + myRig.transmit_relay + + myRig.mute_relay)); + Latch(BPF); +} + +BOOLEAN +getTransmitRelay(void) { + //Get state of TR relay on BPF board + if (myRig.transmit_relay == TR) + return TRUE; + else + return FALSE; +} + +void +setTransmitRelay(BOOLEAN value) { + //If in valid Amateur BandRelay Save and output new TR Relay setting + BYTE tmpLatch = 0; + BOOLEAN tmpATTN = FALSE; + if (value == TRUE) { + if (IsHamBand(myRig.curBandPlan) == TRUE) { + myRig.transmit_relay = TR; // Set to TX + if (getRFE_Enabled()) { + tmpATTN = getATTN_Relay(); + if (getXVTR_Enabled() && (myRig.dds_freq >= 144)) + setXVTR_TR_Relay(myRig.xvtr_tr_logic); //Set XVTR TR to transmit + else + setAMP_Relay(TRUE); //Switch RFE to transmit + + // TODO: REMOVE THE AMP RELAY SWITCH ON THE PRODUCTION RFE + + tmpLatch = (BYTE) (myRig.transmit_relay + myRig.mute_relay); + } else + tmpLatch = + (BYTE) (myRig.band_relay + myRig.transmit_relay + + myRig.mute_relay); + } + } else { + myRig.transmit_relay = 0; // Set to RX + if (getRFE_Enabled()) { + setAMP_Relay(FALSE); + tmpLatch = (BYTE) (myRig.transmit_relay + myRig.mute_relay); + if (getXVTR_Enabled()) + setXVTR_TR_Relay(!myRig.xvtr_tr_logic); // Set XVTR TR to receive + setATTN_Relay(tmpATTN); + } else + tmpLatch = + (BYTE) (myRig.band_relay + myRig.transmit_relay + + myRig.mute_relay); + } + PWrite(tmpLatch); + Latch(BPF); +} + +BOOLEAN +getMuteRelay(void) { +//Get state of MUTE relay on TRX board + if (myRig.mute_relay == MUTE) + return FALSE; + else + return TRUE; +} + +void +setMuteRelay(BOOLEAN value) { + //Mute the speaker relay if TRUE + if (value == TRUE) + myRig.mute_relay = 0; + else + myRig.mute_relay = MUTE; + PWrite((BYTE) + (myRig.band_relay + myRig.transmit_relay + myRig.mute_relay)); + Latch(BPF); +} + +BOOLEAN +getGainRelay(void) { + //Get state of GAIN relay on TRX board + if (myRig.gain_relay == GAIN) + return FALSE; + else + return TRUE; +} + +void +setGainRelay(BOOLEAN value) { //Save and output state of GAIN relay on TRX board + if (value == TRUE) + myRig.gain_relay = 0; // 40dB or 0dB w/RFE + else + myRig.gain_relay = GAIN; // 26dB + PWrite((BYTE) (myRig.external_output + myRig.gain_relay)); + Latch(EXT); +} + +int +getExternalOutput(void) { //Get state of External Control outputs on PIO board + return myRig.external_output; +} + +void +setExternalOutput(int value) { + //Save and output state of External Control outputs on PIO board + myRig.external_output = value; + PWrite((BYTE) (myRig.external_output + myRig.gain_relay)); + Latch(EXT); +} + +double +getDDSClockCorrection(void) { + return myRig.dds_clock_correction; +} + +void +setDDSClockCorrection(double value) { + myRig.dds_clock_correction = value; + CalcClock(); + SetRadioFreq(myRig.dds_freq); +} + +int +getPLLMult(void) { + return myRig.pll_mult; +} + +PRIVATE void +CalcClock(void) { + //sysClock = (pll_mult * dds_clock); + myRig.sysClock = (myRig.pll_mult * myRig.dds_clock) + + myRig.dds_clock_correction; + //Calculates step size for 16 bit frequency step size + myRig.dds_step_size = myRig.sysClock / pow(2.0, 48.0); +} + +void +setPLLMult(int value) { + myRig.pll_mult = value; + CalcClock(); +} + +double +getDDSClock(void) { + return myRig.dds_clock; +} + +void +setDDSClock(double value) { + //Compute internal DDS System Clock and Phase Truncation Elimination Step + myRig.dds_clock = value; + CalcClock(); +} + +BOOLEAN +getIFShift(void) { + return myRig.if_shift; +} + +void +setIFShift(BOOLEAN value) { + //Turns IF shift on and off + myRig.if_shift = value; + if (!myRig.spur_reduction) { + if (myRig.if_shift) { + myRig.OSC_change = -11025.0; + myRig.needs_OSC_change = TRUE; + } else { + myRig.OSC_change = 0.0; + myRig.needs_OSC_change = TRUE; + } + } else + myRig.needs_OSC_change = FALSE; + SetRadioFreq(myRig.dds_freq); +} + +BOOLEAN +getSpurReduction(void) { + return myRig.spur_reduction; +} + +void +setSpurReduction(BOOLEAN value) { + //Turns DDS Phase Truncation Spur reduction on and off + myRig.spur_reduction = value; + if (!myRig.spur_reduction) { + if (myRig.if_shift) { + myRig.OSC_change = -11025.0; + myRig.needs_OSC_change = TRUE; + } else { + myRig.OSC_change = 0.0; + myRig.needs_OSC_change = TRUE; + } + } else + myRig.needs_OSC_change = FALSE; + SetRadioFreq(myRig.dds_freq); +} + +double +getIFFreq(void) { + return myRig.if_freq; +} + +void +setIFFreq(double value) { + //Sets the offset frequency for the IF in MHz + myRig.if_freq = value; +} + +double +getDDSFreq(void) { + return myRig.dds_freq; +} + +void +setDDSFreq(double value) { + myRig.dds_freq = value; + SetRadioFreq(myRig.dds_freq); +} + +int +getSampleRate(void) { + return myRig.sample_rate; +} + +void +setSampleRate(int value) { + myRig.sample_rate = value; + //Compute bandwidth of FFT bin + if (myRig.fft_length > 0) + myRig.FFT_Bin_Size = (myRig.sample_rate / myRig.fft_length) * 1e-6; +} + +int +getFFTLength(void) { + return myRig.fft_length; +} + +void +setFFTLength(int value) { + myRig.fft_length = value; + //Compute bandwidth of FFT bin + if (myRig.fft_length > 0) + myRig.FFT_Bin_Size = (myRig.sample_rate / myRig.fft_length) * 1e-6; +} + +int +getTuneFFT(void) { + return myRig.tune_fft; +} + +double +getTuneFracRel(void) { + return myRig.tune_frac_rel; +} + +double +getVFOOffset(void) { + return myRig.vfo_offset; +} + +void +setVFOOffset(double value) { + myRig.vfo_offset = value; + SetRadioFreq(myRig.dds_freq); +} + +int +getIOUDClock(void) { + return myRig.ioud_clock; +} + +void +setIOUDClock(int value) { + double bitVal, bytVal; + int i; + BYTE lWord; + myRig.ioud_clock = value; //Save value + + bitVal = value; //Compute Numeric Value + + for (i = 24; i >= 0; i -= 8) //Convert to binary strings + { + // bytVal = bitVal / (Math.Pow(2, i)); //Compute binary byte Value + bytVal = bitVal / pow(2.0, (double) i); //Compute binary byte Value + lWord = (BYTE) bytVal; //Truncate fractional portion + bitVal -= lWord * pow(2.0, (double) i); //Reduce value + switch (i) //Write to byte position + { + case 32: + DDSWrite(lWord, 22); + break; + case 16: + DDSWrite(lWord, 23); + break; + case 8: + DDSWrite(lWord, 24); + break; + case 0: + DDSWrite(lWord, 25); + break; + } + } +} + +unsigned short +getDACMult(void) { + return myRig.dac_mult; +} + +void +setDACMult(unsigned short value) { + double bitVal, bytVal; + int i; + BYTE lWord; + //Send new I DAC Multiplier value to DDS + myRig.dac_mult = value; + bitVal = value; //Compute Numeric Value + for (i = 8; i >= 0; i -= 8) //Convert to binary strings + { + bytVal = bitVal / pow(2.0, (double) i); //Compute binary byte Value + lWord = (BYTE) bytVal; //Truncate fractional portion + bitVal -= lWord * pow(2.0, (double) i); //Reduce value + switch (i) { + case 8: + DDSWrite(lWord, 33); + break; + case 0: + DDSWrite(lWord, 34); + break; + } + } + + bitVal = value; //Compute Numeric Value + for (i = 8; i >= 0; i -= 8) //Convert to binary strings + { + bytVal = bitVal / pow(2.0, (double) i); //Compute binary byte Value + lWord = (BYTE) bytVal; //Truncate fractional portion + bitVal -= lWord * pow(2.0, (double) i); //Reduce value + switch (i) { + case 8: + DDSWrite(lWord, 35); + break; + case 0: + DDSWrite(lWord, 36); + break; + } + } +} + +// ====================================================== +// Private Member Functions +// ====================================================== + +PRIVATE void +Delay(void) { + usleep(1000 * myRig.latch_delay); +} + +PRIVATE void +Latch(CtrlPin vCtrlPin) { + unsigned char mask; + //Strobe the specified pin to latch data + switch (vCtrlPin) { + case EXT: + mask = 0xA, ioctl(myRig.fd, PPWCONTROL, &mask); + Delay(); + mask = 0xB, ioctl(myRig.fd, PPWCONTROL, &mask); + break; + case BPF: + mask = 0x9, ioctl(myRig.fd, PPWCONTROL, &mask); + Delay(); + mask = 0xB, ioctl(myRig.fd, PPWCONTROL, &mask); + break; + case DAT: + mask = 0xF, ioctl(myRig.fd, PPWCONTROL, &mask); + Delay(); + mask = 0xB, ioctl(myRig.fd, PPWCONTROL, &mask); + break; + case ADR: + mask = 0x3, ioctl(myRig.fd, PPWCONTROL, &mask); + Delay(); + mask = 0xB, ioctl(myRig.fd, PPWCONTROL, &mask); + break; + } +} + +PRIVATE void +ResetLatches(void) { //Set all latch outputs to logic zero (relays off) + PWrite(0); + Latch(ADR); + Latch(DAT); + + myRig.gain_relay = GAIN; + myRig.external_output = 0; + PWrite((BYTE) (myRig.external_output + myRig.gain_relay)); + Latch(EXT); + + setBandRelay(bsnone); + myRig.transmit_relay = 0; + myRig.mute_relay = MUTE; + + PWrite((BYTE) + (myRig.band_relay + myRig.transmit_relay + myRig.mute_relay)); + Latch(BPF); +} + +PRIVATE void +PWrite(BYTE data) { //Write data Byte to parallel port + ioctl(myRig.fd, PPWDATA, &data); + Delay(); //Delay to allow data line setup +} + +PRIVATE void +ResetDDS(void) { + PWrite(RESET + WRB); //Reset the DDS chip + Latch(ADR); + PWrite(WRB); //Leave WRB high + Latch(ADR); + DDSWrite(COMP_PD, 29); //Power down comparator + //TODO: ADD PLL MULTIPLIER PROPERTY AND LOGIC + DDSWrite(BYPASS_PLL, 30); //Bypass PLL + //DDSWrite(BYPASS_SINC + OSK_EN, 32);//Bypass Inverse Sinc and enable DAC Mult + DDSWrite(BYPASS_SINC, 32); + setDACMult(4095); //Set DAC multiplier value +} + +PRIVATE void +DDSWrite(BYTE data, BYTE addr) { + //Set up data bits + PWrite(data); + Latch(DAT); + + //Set up address bits with WRB high + PWrite((BYTE) (addr + WRB)); + Latch(ADR); + + //Send write command with WRB low + PWrite(addr); + Latch(ADR); + + //Return WRB high + PWrite(WRB); + Latch(ADR); +} + +PRIVATE void +SetRadioFreq(double f) { + double vfoFreq = f; + int i; + // calculate software frequency to program + if (getXVTR_Enabled() && f >= 144 && f <= 146) //If transverter enabled compute 28MHz IF frequency + f -= 116; //Subtract 116MHz (144-28) from VFO display frequency + + if (myRig.if_shift) + f -= myRig.if_freq; // adjust for IF shift + + f += myRig.vfo_offset; // adjust for vfo offset + + unsigned long long int tuning_word = + (unsigned long long int) (f / myRig.sysClock * pow(2.0, 48.0)); + + // start with current tuning word + // clear first bit, low 31 bits; set bit 31 + if (myRig.spur_reduction) { + unsigned long long int sr_tuning_word = + (tuning_word & ~(0x80007fffffffLL)) | 0x000080000000LL; + double software_offset = + (sr_tuning_word - tuning_word) * myRig.dds_step_size; + + if (myRig.if_shift) //Convert the tuning fraction to rel frq + myRig.tune_frac_rel = 1000000.0 * (software_offset) - 11025.0; + else + myRig.tune_frac_rel = 1000000.0 * (software_offset); + myRig.OSC_change = myRig.tune_frac_rel; + myRig.needs_OSC_change = TRUE; + + tuning_word = sr_tuning_word; + } + // program hardware + SetBPF(vfoFreq); + + if (tuning_word != myRig.last_tuning_word) { + //Debug.WriteLine("tuning_word != last_tuning_word"); + //Debug.Write("tuning word: "); + + myRig.last_tuning_word = tuning_word; //save new tuning word + + for (i = 0; i < 6; i++) { + BYTE b = + (BYTE) (tuning_word >> (unsigned long long int) (40 - i * 8)) & + 0xFFLL; + //Debug.Write(b+" "); + DDSWrite(b, (BYTE) (4 + i)); + } + //Debug.WriteLine(""); + } +} + +// ====================================================== +// Public Member Functions +// ====================================================== + +BOOLEAN +InputPin(StatusPin vStatusPin) { //Readback state and mask specified status pin + unsigned char status; + ioctl(myRig.fd, PPRSTATUS, &status); + if (vStatusPin == PIN_11) //Pin 11 is inverted + return (((BYTE) vStatusPin & status) == 0); + else + return (((BYTE) vStatusPin & status) != 0); +} + +BYTE +StatusPort(void) { + unsigned char status; + ioctl(myRig.fd, PPRSTATUS, &status); + return status; +} + +void +RigInit(void) { + setIFShift(TRUE); + + // user setable through the setup form + //DDSClockCorrection = 0.000; + setPLLMult(1); + setDDSClock(200.0); + setIFFreq(0.011025); + setSampleRate(48000); + setFFTLength(4096); + setDACMult(4095); + + // ResetLatches(void); //Reset all control latches + // + // if(RFE_Enabled) + // { + // ResetRFE(void); + // if(XVTR_Enabled) + // XVTR_TR_Relay = TRUE; //Set transverter to receive mode + // } + // ResetDDS(void); //Hardware reset for DDS +} + +void +PowerOn(void) { + // set mute/gain relays based on console + if (getXVTR_Enabled()) + setXVTR_TR_Relay(!myRig.xvtr_tr_logic); + setDDSFreq(myRig.dds_freq); +} + +void +StandBy(void) { + ResetLatches(); + if (getRFE_Enabled()) + ResetRFE(); + if (getXVTR_Enabled()) + setXVTR_TR_Relay(FALSE); + ResetDDS(); +} + +void +SetExt(ExtPin pin) { + //Set the designated external pin high + myRig.external_output += (BYTE) pin; + PWrite((BYTE) (myRig.external_output + myRig.gain_relay)); + Latch(EXT); +} + +void +ResExt(ExtPin pin) { + //Reset the designated external pin high + myRig.external_output -= (BYTE) pin; + PWrite((BYTE) (myRig.external_output + myRig.gain_relay)); + Latch(EXT); +} + +BOOLEAN +PinValue(ExtPin pin) { + //Return TRUE if Pin is set + if ((myRig.external_output & (int) pin) != 0) + return TRUE; + else + return FALSE; +} + +void +SetBPF(double VFOValue) { + if (getRFE_Enabled()) // RFE is present + { + //Use shift registers on RFE to control BPF and LPF banks + if (VFOValue <= 2) // DC to 2MHz + { + SRLoad(IC10, LPF9 + BPF0); + SRLoad(IC9, 0); + } else if (VFOValue <= 4) // 2MHz to 4MHz + { + SRLoad(IC10, BPF1); + SRLoad(IC9, LPF7); + } else if (VFOValue <= 6) // 4MHz to 6MHz + { + SRLoad(IC10, BPF1); + SRLoad(IC9, LPF2); + } else if (VFOValue <= 7.3) // 6MHz to 7.3MHz + { + SRLoad(IC10, BPF2); + SRLoad(IC9, LPF5); + } else if (VFOValue <= 10.2) // 7.3MHz to 10.2MHz + { + SRLoad(IC10, BPF2); + SRLoad(IC9, LPF4); + } else if (VFOValue <= 12) // 10.2MHz to 12MHz + { + SRLoad(IC10, BPF2); + SRLoad(IC9, LPF3); + } else if (VFOValue <= 14.5) // 12MHz to 14.5MHz + { + SRLoad(IC10, BPF3); + SRLoad(IC9, LPF3); + } else if (VFOValue <= 21.5) // 14.5MHz to 21.5MHz + { + SRLoad(IC10, BPF3 + LPF8); + SRLoad(IC9, 0); + } else if (VFOValue <= 24) // 21.5MHz to 24MHz + { + SRLoad(IC10, BPF3); + SRLoad(IC9, LPF6); + } else if (VFOValue <= 30) // 24MHz to 30MHz + { + SRLoad(IC10, BPF4); + SRLoad(IC9, LPF6); + } else if (VFOValue <= 36) // 30MHz to 36MHz + { + SRLoad(IC10, BPF4); + SRLoad(IC9, LPF1); + } else if (VFOValue <= 65) // 36MHz to 65Mhz + { + SRLoad(IC10, BPF5); + SRLoad(IC9, LPF1); + } else if (getXVTR_Enabled() && VFOValue >= 144 && VFOValue <= 146) //28MHz IF for transverter + { + SRLoad(IC10, BPF4); + SRLoad(IC9, LPF6); + setXVTR_Relay(TRUE); + } + if (getXVTR_Enabled() && VFOValue < 144) + setXVTR_Relay(FALSE); + } else // RFE is not present + { + //Select the BPF relays using the high frequency cutoff + if (VFOValue < 2) //DC to 2MHz + setBandRelay(bs0); + else if (VFOValue < 6) //2MHz to 6MHz + setBandRelay(bs1); + else if (VFOValue < 12) //6MHz to 12MHz + setBandRelay(bs2); + else if (VFOValue < 24) //12MHz to 24MHz + setBandRelay(bs3); + else if (VFOValue < 36) //24MHz to 36MHz + setBandRelay(bs4); + else //36MHz to 65Mhz + setBandRelay(bs5); + } +} + +BOOLEAN +IsHamBand(BandPlan b) { + if (myRig.extended) + return TRUE; + + switch (b) { + case IARU1: + if (myRig.dds_freq >= 1.8 && myRig.dds_freq <= 2.0) + return TRUE; + else if (myRig.dds_freq >= 3.5 && myRig.dds_freq <= 4.0) + return TRUE; + else if (myRig.dds_freq == 5.3305) + return TRUE; + else if (myRig.dds_freq == 5.3465) + return TRUE; + else if (myRig.dds_freq == 5.3665) + return TRUE; + else if (myRig.dds_freq == 5.3715) + return TRUE; + else if (myRig.dds_freq == 5.4035) + return TRUE; + else if (myRig.dds_freq >= 7.0 && myRig.dds_freq <= 7.3) + return TRUE; + else if (myRig.dds_freq >= 10.1 && myRig.dds_freq <= 10.15) + return TRUE; + else if (myRig.dds_freq >= 14.0 && myRig.dds_freq <= 14.35) + return TRUE; + else if (myRig.dds_freq >= 18.068 && myRig.dds_freq <= 18.168) + return TRUE; + else if (myRig.dds_freq >= 21.0 && myRig.dds_freq <= 21.45) + return TRUE; + else if (myRig.dds_freq >= 24.89 && myRig.dds_freq <= 24.99) + return TRUE; + else if (myRig.dds_freq >= 21.0 && myRig.dds_freq <= 21.45) + return TRUE; + else if (myRig.dds_freq >= 28.0 && myRig.dds_freq <= 29.7) + return TRUE; + else if (myRig.dds_freq >= 50.0 && myRig.dds_freq <= 54.0) + return TRUE; + else if (myRig.dds_freq >= 144.0 && myRig.dds_freq <= 146.0) { + if (getRFE_Enabled() && getXVTR_Enabled()) + return TRUE; + else + return FALSE; + } else + return FALSE; + default: + return FALSE; + // TODO: Implement other bandplans here + } +} + +void +TestPort(void) { + //Toggle 1 and 0 to each of the four parallel port latches + PWrite(0); + Latch(BPF); + Latch(ADR); + Latch(DAT); + Latch(EXT); + PWrite(255); + Latch(BPF); + Latch(ADR); + Latch(DAT); + Latch(EXT); +} + +void +RCKStrobe(BOOLEAN ClearReg, RFE_RCK Reg) { + //Strobe the RFE 1:4 decoder output to transfer contents of shift register to output latches + if (ClearReg) //Clear the shift register contents + PWrite((BYTE) (Reg)); //Strobe decoder output low and clear register + else + PWrite((BYTE) (SCLR_NOT + Reg + myRig.transmit_relay + myRig.mute_relay)); //Strobe decoder output low + Latch(BPF); + PWrite((BYTE) (SCLR_NOT + DCDR_NE + myRig.transmit_relay + myRig.mute_relay)); //Disable 1:4 decoder outputs + Latch(BPF); +} + +void +SRLoad(RFE_RCK Reg, int Data) { + //Shift data into shift registers on RFE + int choose[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; + int i; + + for (i = 0; i < 8; i++) { + int mask = choose[i]; //Mask the current bit + if ((mask & Data) == 0) //Current bit = 0 + { + PWrite((BYTE) (SCLR_NOT + DCDR_NE + myRig.transmit_relay + myRig.mute_relay)); //Output 0 bit + Latch(BPF); + PWrite((BYTE) (SCLR_NOT + DCDR_NE + SCK + myRig.transmit_relay + myRig.mute_relay)); //Clock 0 into shift register + } else //Current bit = 1 + { + PWrite((BYTE) (SCLR_NOT + DCDR_NE + SER + myRig.transmit_relay + myRig.mute_relay)); //Output 1 bit + Latch(BPF); + PWrite((BYTE) (SCLR_NOT + DCDR_NE + SER + SCK + myRig.transmit_relay + myRig.mute_relay)); //Clock 1 into shift register + } + Latch(BPF); + PWrite((BYTE) (SCLR_NOT + DCDR_NE + myRig.transmit_relay + myRig.mute_relay)); //Return SCK low + Latch(BPF); + } + RCKStrobe(FALSE, Reg); //Strobe Register Clock +} + +void +ResetRFE(void) { + //Reset all RFE shift registers to zero output + RCKStrobe(TRUE, IC11); + RCKStrobe(TRUE, IC7); + RCKStrobe(TRUE, IC10); + RCKStrobe(TRUE, IC9); +} + +BOOLEAN +getAMP_Relay(void) { + return (myRig.m_IC7_Memory & AMP_RLYS) != 0; +} + +void +setAMP_Relay(BOOLEAN value) { + //Set or reset LNA relay + if (value) + myRig.m_IC7_Memory = (myRig.m_IC7_Memory | AMP_RLYS); + else + myRig.m_IC7_Memory = (myRig.m_IC7_Memory & (AMP_RLYS ^ 255)); + SRLoad(IC7, myRig.m_IC7_Memory); +} + + +BOOLEAN +getATTN_Relay(void) { + return (myRig.m_IC7_Memory & ATTN_RLY) != 0; +} + +void +setATTN_Relay(BOOLEAN value) { + //Set or reset attenuator relay + if (value) + myRig.m_IC7_Memory = (myRig.m_IC7_Memory | ATTN_RLY); + else + myRig.m_IC7_Memory = (myRig.m_IC7_Memory & (ATTN_RLY ^ 255)); + SRLoad(IC7, myRig.m_IC7_Memory); +} + +BOOLEAN +getXVTR_TR_Relay(void) { + return (myRig.m_IC7_Memory & XVTR_TR_RLY) != 0; +} + +void +setXVTR_TR_Relay(BOOLEAN value) { + //Set or reset transverter relay + if (value) + myRig.m_IC7_Memory = (myRig.m_IC7_Memory | XVTR_TR_RLY); + else + myRig.m_IC7_Memory = myRig.m_IC7_Memory & (XVTR_TR_RLY ^ 255); + SRLoad(IC7, myRig.m_IC7_Memory); +} + +BOOLEAN +getXVTR_Relay(void) { + return (myRig.m_IC7_Memory & XVTR_RLY) != 0; +} + +void +setXVTR_Relay(BOOLEAN value) { + //Set or reset XVTR 8R (TR) relay + if (value) + myRig.m_IC7_Memory = myRig.m_IC7_Memory | XVTR_RLY; + else + myRig.m_IC7_Memory = myRig.m_IC7_Memory & (XVTR_RLY ^ 255); + SRLoad(IC7, myRig.m_IC7_Memory); +} + +BOOLEAN +getIMPULSE_Relay(void) { + return (myRig.m_IC7_Memory & IMPULSE_RLY) != 0; +} + +void +setIMPULSE_Relay(BOOLEAN value) { + //Set or reset Impulse relay + if (value) + myRig.m_IC7_Memory = myRig.m_IC7_Memory | IMPULSE_RLY; + else + myRig.m_IC7_Memory = myRig.m_IC7_Memory & (IMPULSE_RLY ^ 255); + SRLoad(IC7, myRig.m_IC7_Memory); +} + +void +Impulse(void) { + //Send a single impulse to the QSD + SRLoad(IC7, (myRig.m_IC7_Memory | 128)); + SRLoad(IC7, myRig.m_IC7_Memory); +} + +BOOLEAN +openRig(char *port) { + int mode = IEEE1284_MODE_COMPAT; + if (!port) port = "/dev/parport0"; + if ((myRig.fd = open(port, O_RDWR)) < 0) { + perror("open parallel port"); + return FALSE; + } + ioctl(myRig.fd, PPCLAIM); + ioctl(myRig.fd, PPSETMODE, &mode); + return TRUE; +} + +void +closeRig(void) { + ioctl(myRig.fd, PPRELEASE); + close(myRig.fd); +} diff --git a/pyhw/hardware.h b/pyhw/hardware.h new file mode 100644 index 0000000..99e81c7 --- /dev/null +++ b/pyhw/hardware.h @@ -0,0 +1,318 @@ +/* hardware.h */ + +#ifndef _HARDWARE_H +#define _HARDWARE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef PRIVATE +#define PRIVATE static +#endif + +typedef int BOOLEAN; +typedef unsigned char BYTE; + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +// ====================================================== +// Constants and PRIVATE Variables +// ====================================================== + +typedef enum _bandplan +{ + IARU1 = 1, + IARU2 = 2, + IARU3 = 3, +} BandPlan; + +//Constants for BPF relays + +typedef enum _bandsetting +{ + bsnone = 0, //none + bs0 = 0x01, //2.5MHz LPF + bs1 = 0x02, //2-6MHz BPF + bs2 = 0x08, //5-12MHz BPF + bs3 = 0x04, //10-24MHz BPF + bs4 = 0x10, //20-40MHz BPF + bs5 = 0x20, //35-60MHz BPF +} BandSetting; + +//PIO Control Pin Numbers + +typedef enum _ctrlpin +{ + EXT = 1, //C0 + BPF = 14, //C1 + DAT = 16, //C2 + ADR = 17, //C3 +} CtrlPin; + +//PIO Status Mask + +typedef enum _statuspin +{ + PIN_12 = 0x08, //S3 + Dash = 0x10, //S4 + Dot = 0x20, //S5 + PTT = 0x40, //S6 + PIN_11 = 0x80, //S7 +} StatusPin; + +//External control port mask + +typedef enum _extpin +{ + P1 = 0x01, + P2 = 0x02, + P3 = 0x04, + P4 = 0x08, + P5 = 0x10, + P6 = 0x20, + P7 = 0x40, +} ExtPin; + +//BEGIN RFE CONTROLS ===================================================== + +//Control and data lines for RFE serial decoders +#define SER 1 +#define SCK 2 +#define SCLR_NOT 4 +#define DCDR_NE 32 + +//RFE 1:4 Decoder (74HC139) values to drive shift register RCK lines +typedef enum _rfe_rck +{ + IC11 = 0, + IC7 = 8, + IC9 = 24, + IC10 = 16, +} RFE_RCK; + +//RFE control constants +#define LPF0 1 //On board low pass filter relays +#define LPF1 2 +#define LPF2 4 +#define LPF3 8 +#define LPF4 16 +#define LPF5 32 +#define LPF6 64 +#define LPF7 128 +#define LPF8 1 +#define LPF9 2 + +#define BPF0 128 //Band pass filter relays +#define BPF1 64 +#define BPF2 16 //Note: BPF2 and BPF3 are reverse order +#define BPF3 32 //on BPF board +#define BPF4 8 +#define BPF5 4 + +#define PAF0 1 //Power Amplifier low pass filters +#define PAF1 2 +#define PAF2 4 +#define PAF3 8 +#define PAF4 16 +#define PAF5 32 +#define PAFR 64 //Power amplifier TR relay + +#define ATUCTL 128 //Automatic Tuning Unit control + +#define AMP_RLYS 3 //Controls both AMP1 and AMP2 relays +#define XVTR_RLY 8 //Switches 2M transverter switching relayinto signal path +#define ATTN_RLY 16 //Attenuator relay +#define XVTR_TR_RLY 4 //2M transverter TR relay (on for RX) XVRX on schematic +#define IMPULSE_RLY 32 //Impulse circuit switching relay +#define SPARE_CTRL 64 //Spare control line to PA + + +//Constants latch outputs + +#define TR 0x40 +#define MUTE 0x80 +#define GAIN 0x80 +#define WRB 0x40 +#define RESET 0x80 + +//DDS Control Constants +#define COMP_PD 0x10 //DDS Comparator power down +#define DIG_PD 0x01 //DDS Digital Power down +#define BYPASS_PLL 0x20 //Bypass DDS PLL +#define INT_IOUD 0x01 //Internal IO Update +#define OSK_EN 0x20 //Offset Shift Keying enable +#define OSK_INT 0x10 //Offset Shift Keying +#define BYPASS_SINC 0x40 //Bypass Inverse Sinc Filter +#define PLL_RANGE 0x40 //Set PLL Range + +typedef struct _rig +{ + //PIO register base address +// unsigned short baseAdr; + double min_freq; // minimum allowable tuning frequency + double max_freq; // maximum allowable tuning frequency + unsigned radio_number; + unsigned short Adr; + int m_IC7_Memory; + //RFE relay memory + BOOLEAN rfe_enabled; //True if RFE board is enabled + BOOLEAN xvtr_enabled; //Transverter is enabled + BOOLEAN extended; + BOOLEAN xvtr_tr_logic; + + // END RFE controls + //Latch Memories + + BandSetting band_relay; + int external_output; + int mute_relay; + int transmit_relay; + int gain_relay; + int latch_delay; + + //DDS Clock properties + double dds_clock; + int pll_mult; + double dds_clock_correction; + double sysClock; + int ioud_clock; + unsigned short dac_mult; + + //DDS Frequency Control properties + double dds_freq; + double if_freq; + BOOLEAN if_shift; + BOOLEAN spur_reduction; + double dds_step_size; + int sample_rate; + int fft_length; + double FFT_Bin_Size; + int tune_fft; + double tune_frac_rel; + double vfo_offset; + double last_VFO; + + //Current Bandplan + BandPlan curBandPlan; + + double TWO_TO_THE_48_DIVIDED_BY_200; + long last_tuning_word; + + int fd; + BOOLEAN needs_OSC_change; + double OSC_change; +} Rig; + +extern Rig myRig; + +extern BOOLEAN openRig(char *port); +extern void closeRig(void); + +extern BOOLEAN getExtended(void); +extern void setExtended(BOOLEAN value); +extern BOOLEAN getRFE_Enabled(void); +extern void setRFE_Enabled(BOOLEAN value); +extern BOOLEAN getXVTR_Enabled(void); +extern void setXVTR_Enabled(BOOLEAN value); +extern BOOLEAN getXVTR_TR_Logic(void); +extern void setXVTR_TR_Logic(BOOLEAN value); +extern int getLatchDelay(void); +extern void setLatchDelay(int value); +extern double getMinFreq(void); +extern double getMaxFreq(void); +//extern unsigned short getBaseAddr(void); +//extern void setBaseAddr(unsigned short value); +extern BandSetting getBandRelay(void); +extern void setBandRelay(BandSetting value); +extern BOOLEAN getTransmitRelay(void); +extern void setTransmitRelay(BOOLEAN value); +extern BOOLEAN getMuteRelay(void); +extern void setMuteRelay(BOOLEAN value); +extern BOOLEAN getGainRelay(void); +extern void setGainRelay(BOOLEAN value); +extern int getExternalOutput(void); +extern void setExternalOutput(int value); +extern double getDDSClockCorrection(void); +extern void setDDSClockCorrection(double value); +extern int getPLLMult(void); +extern void setPLLMult(int value); +extern double getDDSClock(void); +extern void setDDSClock(double value); +extern BOOLEAN getIFShift(void); +extern void setIFShift(BOOLEAN value); +extern BOOLEAN getSpurReduction(void); +extern void setSpurReduction(BOOLEAN value); +extern double getIFFreq(void); +extern void setIFFreq(double value); +extern double getDDSFreq(void); +extern void setDDSFreq(double value); +extern int getSampleRate(void); +extern void setSampleRate(int value); +extern int getFFTLength(void); +extern void setFFTLength(int value); +extern int getTuneFFT(void); +extern double getTuneFracRel(void); +extern double getVFOOffset(void); +extern void setVFOOffset(double value); +extern int getIOUDClock(void); +extern void setIOUDClock(int value); +extern unsigned short getDACMult(void); +extern void setDACMult(unsigned short value); +extern BOOLEAN InputPin(StatusPin vStatusPin); +extern BYTE StatusPort(void); +extern void RigInit(void); +extern void PowerOn(void); +extern void StandBy(void); +extern void SetExt(ExtPin pin); +extern void ResExt(ExtPin pin); +extern BOOLEAN PinValue(ExtPin pin); +extern void SetBPF(double VFOValue); +extern BOOLEAN IsHamBand(BandPlan b); +extern void TestPort(void); +extern void RCKStrobe(BOOLEAN ClearReg, RFE_RCK Reg); +extern void SRLoad(RFE_RCK Reg, int Data); +extern void ResetRFE(void); +extern BOOLEAN getAMP_Relay(void); +extern void setAMP_Relay(BOOLEAN value); +extern BOOLEAN getATTN_Relay(void); +extern void setATTN_Relay(BOOLEAN value); +extern BOOLEAN getXVTR_TR_Relay(void); +extern void setXVTR_TR_Relay(BOOLEAN value); +extern BOOLEAN getXVTR_Relay(void); +extern void setXVTR_Relay(BOOLEAN value); +extern BOOLEAN getIMPULSE_Relay(void); +extern void setIMPULSE_Relay(BOOLEAN); +extern void Impulse(void); +#endif diff --git a/pyhw/hardware.i b/pyhw/hardware.i new file mode 100644 index 0000000..99a24ff --- /dev/null +++ b/pyhw/hardware.i @@ -0,0 +1,5 @@ +%module sdr1khw +%{ +#include "hardware.h" +%} +%include "hardware.h" diff --git a/pyhw/sdr1k-setup.py b/pyhw/sdr1k-setup.py new file mode 100644 index 0000000..ba50777 --- /dev/null +++ b/pyhw/sdr1k-setup.py @@ -0,0 +1,19 @@ +from sdr1khw import * + +openRig("/dev/parport0") +setRFE_Enabled(TRUE) +ResetRFE() +setXVTR_Enabled(FALSE) +setExtended(FALSE) +setXVTR_TR_Logic(FALSE) +setBandRelay(bs0) +RigInit() +setVFOOffset(0.0) +setATTN_Relay(TRUE) +setTransmitRelay(FALSE) +setGainRelay(TRUE) +setSpurReduction(FALSE) +setExternalOutput(FALSE) +setDDSClockCorrection(0.0) +setDDSFreq(13.845) +setMuteRelay(FALSE)