From 803a0cf5a0e09fc8bb1ed093b62689ae19f92e61 Mon Sep 17 00:00:00 2001
From: dttsp <dttsp>
Date: Fri, 3 Jun 2005 13:14:32 +0000
Subject: [PATCH] Major update

---
 jDttSP/Makefile           |  16 ++-
 jDttSP/banal.h            |   1 +
 jDttSP/command-vocabulary | 142 +++++++++++++--------------
 jDttSP/common.h           |   2 +
 jDttSP/dcblock.c          | 199 ++++++++++++++++++++++++++++++++++++++
 jDttSP/dcblock.h          |  64 ++++++++++++
 jDttSP/defs.h             |   1 +
 jDttSP/digitalagc.c       | 109 +++++++++++----------
 jDttSP/digitalagc.h       |   6 +-
 jDttSP/enums.m4           |  23 +++++
 jDttSP/keyer.c            | 110 ---------------------
 jDttSP/local.h            |   2 +-
 jDttSP/main.c             |  10 ++
 jDttSP/meter.c            |   1 -
 jDttSP/meter.h            |   6 +-
 jDttSP/mkchan.c           |  84 ----------------
 jDttSP/ringb.c            |  14 +++
 jDttSP/ringb.h            |  14 +++
 jDttSP/sdr.c              | 156 ++++++++++++++++++++----------
 jDttSP/sdrexport.h        |  39 ++++++--
 jDttSP/spectrum.c         |  32 +++---
 jDttSP/spectrum.h         |   3 +-
 jDttSP/update.c           |  92 ++++++++++++------
 jDttSP/wscompand.c        | 108 +++++++++++++++++++++
 jDttSP/wscompand.h        |  60 ++++++++++++
 25 files changed, 868 insertions(+), 426 deletions(-)
 create mode 100644 jDttSP/dcblock.c
 create mode 100644 jDttSP/dcblock.h
 delete mode 100644 jDttSP/mkchan.c
 create mode 100644 jDttSP/wscompand.c
 create mode 100644 jDttSP/wscompand.h

diff --git a/jDttSP/Makefile b/jDttSP/Makefile
index 8130ea6..86b1699 100644
--- a/jDttSP/Makefile
+++ b/jDttSP/Makefile
@@ -1,5 +1,5 @@
-#CFLAGS = -g -O3 -I. -I/usr/local/include
-CFLAGS = -g -I. -I/usr/local/include
+CFLAGS = -g -O3 -I. -I/usr/local/include
+#CFLAGS = -g -I. -I/usr/local/include
 LIBS = -L/usr/local/lib -ljack -lpthread -lgsl -lgslcblas -lfftw -lm
 #LIBS = -lefence -L/usr/local/lib -ljack -lpthread -lgsl -lgslcblas -lfftw -lm
 
@@ -13,6 +13,7 @@ OBJ =	am_demod.o\
 	correctIQ.o\
 	crc16.o\
 	cxops.o\
+	dcblock.o\
 	digitalagc.o\
 	fastrig.o\
 	filter.o\
@@ -31,6 +32,7 @@ OBJ =	am_demod.o\
 	spottone.o\
 	thunk.o\
 	window.o\
+	wscompand.o\
 	update.o
 
 KOBJ = oscillator.o cwtones.o chan.o ringb.o banal.o bufvec.o splitfields.o cxops.o
@@ -40,13 +42,14 @@ jsdr:	main.o $(OBJ)
 
 all:	jsdr ipc metermon specmon keyd keyb
 
-keyd:	keyd.o keyer.o $(KOBJ)
-	$(CC) -o keyd keyd.o keyer.o $(KOBJ) $(LIBS)
+keyd:	keyd.o keyer.o keyerio.o $(KOBJ)
+	$(CC) -o keyd keyd.o keyer.o keyerio.o $(KOBJ) $(LIBS)
 
-keyb:	keyb.o keyer.o $(KOBJ)
+keyb:	keyb.o keyer.o keyerio.o $(KOBJ)
 	$(CC) -o keyb keyb.o keyer.o $(KOBJ) $(LIBS)
 
 
+
 $(OBJ): sdrexport.h
 
 metermon:	metermon.o 
@@ -78,3 +81,6 @@ staticlib:	$(OBJ)
 # sharedlib:	$(OBJ)
 # 	gcc -shared -Wl,-soname,$(sharedlib) -o $(sharedlib) $(OBJ) -lc
 
+keybun:	keybun.o keyer.o keyerio.o $(KOBJ)
+	$(CC) -o keybun keybun.o keyer.o $(KOBJ) $(LIBS)
+
diff --git a/jDttSP/banal.h b/jDttSP/banal.h
index e6af09b..1235232 100644
--- a/jDttSP/banal.h
+++ b/jDttSP/banal.h
@@ -37,6 +37,7 @@
 #define _banal_h
 
 #include <fromsys.h>
+#include <defs.h>
 #include <datatypes.h>
 
 #ifndef min
diff --git a/jDttSP/command-vocabulary b/jDttSP/command-vocabulary
index 448f6d1..458c7cb 100644
--- a/jDttSP/command-vocabulary
+++ b/jDttSP/command-vocabulary
@@ -1,70 +1,72 @@
-[TRX] indicates optional arg (RX or TX), RX default
-T|F indicates TRUE or FALSE
-
-If first char of command is '-', eg
--setNR ON
-command is not echoed to log output.x
-
-setFilter low-freq high-freq TRX
-setMode mode [TRX]	// mode = USB, LSB, CWL, CWU, etc.
-setOsc freq [TRX]	// freq in Hz (float)
-setSampleRate rate	// Hz (float)
-setNR T|F		// on/off, RX only
-setANF T|F		// on/off, RX only
-setNB T|F		// on/off, RX only
-setBIN T|F		// binaural mode, on/off, RX only
-setNBvals thresh	// float, RX only
-setfixedAGC gain [TRX]	// float
-setRXAGC T|F		// on/off
-setRXAGCCompression lev	// float
-setRXAGCHang dur	// float
-setRXAGCLimit lim	// float
-setTXAGC T|F		// on/off
-setTXAGCCompression lev	// float
-setTXAGCHang dur	// float
-setTXAGCLimit lim	// float
-setTXSpeechCompression T|F	// on/off
-setTXSpeechCompressionGain gain	// float
-setRXEQ <bandspec>	// f0 dB0 f1 dB1 f2 dB2 ... fN
-setTXEQ <bandspec>	// 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
-setRXAGC mode		// mode = agcOFF, agcSLOW, etc.
-setANFvals taps delay gain leak	// int, int, float, float, RX only
-setNRvals taps delay gain leak	// int, int, float, float, RX only
-setcorrectIQ phase gain	// int, int
-setcorrectIQphase phase	// int
-setcorrectIQgain gain	// int
-setSquelch lev		// float, gain, RX only; default -30dB
-setSquelchSt T|F	// on/off, RX only
-setTRX trx		// trx = RX|TX
-setRunState state	// RUN_MUTE, RUN_PASS, RUN_PLAY
-setRXPreScl T|F		// on/off
-setRXPreSclVal valQ	// dB
-setTXPreScl T|F		// on/off
-setTXPreSclVal valQ	// dB
-setRXPostScl T|F	// on/off
-setRXPostSclVal valQ	// dB
-setTXPostScl T|F	// on/off
-setTXPostSclVal valQ	// dB
-setSWCH trx [zap]	// trx = RX|TX, int (always zaps at least 1)
-setSpotToneVals gain freq rise fall // dB, Hz, msec, msec [-12, 700, 5, 5]
-setSpotTone T|F		// turn on, off
-setFinished		// shutdown gracefully
-setRXListen rx		// tell receiver rx to listen to commands to follow
-setRXOn [rx]		// turn currently listening receiver on, or receiver rx
-setRXOff [rx]		// turn currently listening receiver off, or receiver rx
-setRXPan pos		// set azimuth for currently listening receiver to pos (0...1)
-setAuxMixGain [gain [trx]]	// set mixing level for aux inputs
-setAuxMixSt [flag [trx]]	// set aux input mix on/off
-
-setMeterType type [trx] // set meter type for trx, default rx
-			// types: SIG, AVG, REAL, IMAG; default SIG
-setSpectrumType [type [scale [rx]]] // set spectrum type, scale, which rx
-			// types: SEMI_RAW, PRE_FILT, POST_FILT (deflt POST)
-			// scale: PWR, MAG (dflt PWR)
-			// which rx dflt 0
-reqMeter [label]	// sends entire rx or tx meter block to METERPATH
-reqSpectrum [label]	// sends current spec snapshot to SPECPATH
-
+// [TRX] indicates optional arg (RX or TX), RX default
+// T|F indicates TRUE or FALSE
+// 
+// If first char of command is '-', eg
+// -setNR ON
+// command is not echoed to log output.
+// 
+// setFilter low-freq high-freq TRX
+// setMode mode [TRX]	// mode = USB, LSB, CWL, CWU, etc.
+// setOsc freq [TRX]	// freq in Hz (float)
+// setSampleRate rate	// Hz (float)
+// setNR T|F		// on/off, RX only
+// setANF T|F		// on/off, RX only
+// setNB T|F		// on/off, RX only
+// setBIN T|F		// binaural mode, on/off, RX only
+// setNBvals thresh	// float, RX only
+// setfixedAGC gain [TRX]	// float
+// setRXAGC T|F		// on/off
+// setRXAGCCompression lev	// float
+// setRXAGCHang dur	// float
+// setRXAGCLimit lim	// float
+// setTXAGC T|F		// on/off
+// setTXAGCCompression lev	// float
+// setTXAGCHang dur	// float
+// setTXAGCLimit lim	// float
+// setTXSpeechCompression T|F	// on/off
+// setTXSpeechCompressionGain gain	// float
+// setRXEQ <bandspec>	// f0 dB0 f1 dB1 f2 dB2 ... fN
+// setTXEQ <bandspec>	// 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
+// setRXAGC mode		// mode = agcOFF, agcSLOW, etc.
+// setANFvals taps delay gain leak	// int, int, float, float, RX only
+// setNRvals taps delay gain leak	// int, int, float, float, RX only
+// setcorrectIQ phase gain	// int, int
+// setcorrectIQphase phase	// int
+// setcorrectIQgain gain	// int
+// setSquelch lev		// float, gain, RX only; default -30dB
+// setSquelchSt T|F	// on/off, RX only
+// setTRX trx		// trx = RX|TX
+// setRunState state	// RUN_MUTE, RUN_PASS, RUN_PLAY
+// setRXPreScl T|F		// on/off
+// setRXPreSclVal valQ	// dB
+// setTXPreScl T|F		// on/off
+// setTXPreSclVal valQ	// dB
+// setRXPostScl T|F	// on/off
+// setRXPostSclVal valQ	// dB
+// setTXPostScl T|F	// on/off
+// setTXPostSclVal valQ	// dB
+// setSWCH trx [zap]	// trx = RX|TX, int (always zaps at least 1)
+// setSpotToneVals gain freq rise fall // dB, Hz, msec, msec [-12, 700, 5, 5]
+// setSpotTone T|F		// turn on, off
+// setFinished		// shutdown gracefully
+// setRXListen rx		// tell receiver rx to listen to commands to follow
+// setRXOn [rx]		// turn currently listening receiver on, or receiver rx
+// setRXOff [rx]		// turn currently listening receiver off, or receiver rx
+// setRXPan pos		// set azimuth for currently listening receiver to pos (0...1)
+// setAuxMixGain [gain [trx]]	// set mixing level for aux inputs
+// setAuxMixSt [flag [trx]]	// set aux input mix on/off
+// setCompandSt [T|F [trx]]	// *** NB *** trx dflt TX!!!
+// setCompand fac [trx]	// probably > 0 for RX, < 0 for TX
+// setMeterType type [trx] // set meter type for trx, default rx
+// 			// types: SIG, AVG, REAL, IMAG; default SIG
+// setSpectrumType [type [scale [rx]]] // set spectrum type, scale, which rx
+// 			// types: SEMI_RAW, PRE_FILT, POST_FILT (dflt POST)
+// 			// scale: PWR, MAG (dflt PWR)
+// 			// which rx dflt 0
+// =========================================================================
+// reqMeter [label]	// sends entire rx or tx meter block to METERPATH
+// reqSpectrum [label]	// sends current spec snapshot to SPECPATH
+// 
diff --git a/jDttSP/common.h b/jDttSP/common.h
index e31c20c..a7fadfe 100644
--- a/jDttSP/common.h
+++ b/jDttSP/common.h
@@ -55,7 +55,9 @@ Bridgewater, NJ 08807
 #include <noiseblanker.h>
 #include <correctIQ.h>
 #include <crc16.h>
+#include <dcblock.h>
 #include <speechproc.h>
+#include <wscompand.h>
 #include <spottone.h>
 #include <update.h>
 #include <meter.h>
diff --git a/jDttSP/dcblock.c b/jDttSP/dcblock.c
new file mode 100644
index 0000000..bc0cab4
--- /dev/null
+++ b/jDttSP/dcblock.c
@@ -0,0 +1,199 @@
+// dcblock.h
+/*
+This file is part of a program that implements a Software-Defined Radio.
+
+Copyright (C) 2004-2005 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 <dcblock.h>
+
+// NB may have to ramify this a little
+// for other sampling rates; maybe not  
+
+PRIVATE REAL
+butterworth_hipass_100_2(REAL xin, REAL *xv, REAL *yv),
+butterworth_hipass_100_4(REAL xin, REAL *xv, REAL *yv),
+butterworth_hipass_100_6(REAL xin, REAL *xv, REAL *yv),
+butterworth_hipass_100_8(REAL xin, REAL *xv, REAL *yv);
+
+void
+DCBlock(DCBlocker dcb) {
+  int i;
+  REAL x, y;
+
+  switch (dcb->lev) {
+
+  case DCB_LOW:
+    for (i = 0; i < CXBsize(dcb->buf); i++) {
+      x = CXBreal(dcb->buf, i);
+      y = butterworth_hipass_100_2(x, dcb->old.inp, dcb->old.out);
+      CXBdata(dcb->buf, i) = Cmplx(y, 0.0);
+    }
+    break;
+
+  case DCB_MED:
+    for (i = 0; i < CXBsize(dcb->buf); i++) {
+      x = CXBreal(dcb->buf, i);
+      y = butterworth_hipass_100_4(x, dcb->old.inp, dcb->old.out);
+      CXBdata(dcb->buf, i) = Cmplx(y, 0.0);
+    }
+    break;
+
+  case DCB_HIGH:
+    for (i = 0; i < CXBsize(dcb->buf); i++) {
+      x = CXBreal(dcb->buf, i);
+      y = butterworth_hipass_100_6(x, dcb->old.inp, dcb->old.out);
+      CXBdata(dcb->buf, i) = Cmplx(y, 0.0);
+    }
+    break;
+
+  case DCB_SUPER:
+    for (i = 0; i < CXBsize(dcb->buf); i++) {
+      x = CXBreal(dcb->buf, i);
+      y = butterworth_hipass_100_8(x, dcb->old.inp, dcb->old.out);
+      CXBdata(dcb->buf, i) = Cmplx(y, 0.0);
+    }
+    break;
+
+  default:
+    break;
+  }
+}
+
+void
+resetDCBlocker(DCBlocker dcb, int lev) {
+  memset((char *) dcb->old.inp, 0, BLKMEM * sizeof(REAL));
+  memset((char *) dcb->old.out, 0, BLKMEM * sizeof(REAL));
+  dcb->lev = lev;
+}
+
+DCBlocker
+newDCBlocker(int lev, CXB buf) {
+  DCBlocker dcb =
+    (DCBlocker) safealloc(1, sizeof(DCBlockerInfo), "DCBlocker");
+  dcb->buf = newCXB(CXBsize(buf), CXBbase(buf), "DCBlocker");
+  dcb->lev = lev;
+  return dcb;
+}
+
+void
+delDCBlocker(DCBlocker dcb) {
+  if (dcb) {
+    delCXB(dcb->buf);
+    safefree((char *) dcb);
+  }
+}
+
+// f == 0.002083 == 100 Hz at 48k
+
+PRIVATE REAL
+butterworth_hipass_100_2(REAL xin, REAL *xv, REAL *yv) {
+  int i;
+
+  for (i = 1; i < 2; i++)
+    xv[i - 1] = xv[i], yv[i - 1] = yv[i];
+
+  xv[2] = xin / 1.009297482;
+
+  yv[2] =             (xv[0] + xv[2])
+    + -2.0          *  xv[1]
+    + -0.9816611902 *  yv[0]
+    +  1.9814914708 *  yv[1];
+
+  return yv[2];
+}
+
+PRIVATE REAL
+butterworth_hipass_100_4(REAL xin, REAL *xv, REAL *yv) {
+  int i;
+
+  for (i = 1; i < 4; i++)
+    xv[i - 1] = xv[i], yv[i - 1] = yv[i];
+
+  xv[4] = xin / 1.017247322;
+
+  yv[4] =             (xv[0] + xv[4])
+    + -4.0          * (xv[1] + xv[3])
+    +  6.0          *  xv[2]
+    + -0.9663776767 *  yv[0]
+    +  3.8985609655 *  yv[1]
+    + -5.8979831706 *  yv[2]
+    +  3.9657998529 *  yv[3];
+
+  return yv[4];
+}
+
+PRIVATE REAL
+butterworth_hipass_100_6(REAL xin, REAL *xv, REAL *yv) {
+  int i;
+
+  for (i = 1; i < 6; i++)
+    xv[i - 1] = xv[i], yv[i - 1] = yv[i];
+
+  xv[6] = xin / 1.025606415;
+
+  yv[6] =              (xv[0] + xv[6])
+    +  -6.0          * (xv[1] + xv[5])
+    +  15.0          * (xv[2] + xv[4])
+    + -20.0          *  xv[3]
+    +  -0.9506891622 *  yv[0]
+    +   5.7522090378 *  yv[1]
+    + -14.5019247580 *  yv[2]
+    +  19.4994114580 *  yv[3]
+    + -14.7484389800 *  yv[4]
+    +   5.9494324049 *  yv[5];
+
+  return yv[6];
+}
+
+PRIVATE REAL
+butterworth_hipass_100_8(REAL xin, REAL *xv, REAL *yv) {
+  int i;
+
+  for (i = 1; i < 8; i++)
+    xv[i - 1] = xv[i], yv[i - 1] = yv[i];
+
+  xv[8] = xin / 1.034112352;
+
+  yv[8] =              (xv[0] + xv[8])
+    +  -8.0          * (xv[1] + xv[7])
+    +  28.0          * (xv[2] + xv[6])
+    + -56.0          * (xv[3] + xv[5])
+    +  70.0          *  xv[4]
+    +  -0.9351139781 *  yv[0]
+    +   7.5436450525 *  yv[1]
+    + -26.6244301320 *  yv[2]
+    +  53.6964633920 *  yv[3]
+    + -67.6854640540 *  yv[4]
+    +  54.6046308830 *  yv[5]
+    + -27.5326449810 *  yv[6]
+    +   7.9329138172 *  yv[7];
+
+  return yv[8];
+}
diff --git a/jDttSP/dcblock.h b/jDttSP/dcblock.h
new file mode 100644
index 0000000..1fa3e4a
--- /dev/null
+++ b/jDttSP/dcblock.h
@@ -0,0 +1,64 @@
+// dcblock.h
+/*
+This file is part of a program that implements a Software-Defined Radio.
+
+Copyright (C) 2004-2005 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 _dcblock_h
+#define _dcblock_h
+
+#include <fromsys.h>
+#include <defs.h>
+#include <banal.h>
+#include <splitfields.h>
+#include <datatypes.h>
+#include <bufvec.h>
+#include <cxops.h>
+
+#define BLKMEM (9)
+#define DCB_LOW (0)
+#define DCB_MED (1)
+#define DCB_HIGH (2)
+#define DCB_SUPER (3)
+
+typedef struct _dcblocker {
+  struct {
+    REAL inp[BLKMEM], out[BLKMEM];
+  } old;
+  int lev;
+  CXB buf;
+} DCBlockerInfo, *DCBlocker;
+
+extern void DCBlock(DCBlocker dcb);
+extern void resetDCBlocker(DCBlocker dcb, int lev);
+extern DCBlocker newDCBlocker(int lev, CXB buf);
+extern void delDCBlocker(DCBlocker dcb);
+
+#endif
diff --git a/jDttSP/defs.h b/jDttSP/defs.h
index b75f456..283337e 100644
--- a/jDttSP/defs.h
+++ b/jDttSP/defs.h
@@ -41,6 +41,7 @@ Bridgewater, NJ 08807
 #define DEFSIZE (2048)
 #define DEFMODE (SAM)
 #define DEFSPEC (4096)
+#define DEFCOMP (4096)
 
 #define MAXRX (4)
 
diff --git a/jDttSP/digitalagc.c b/jDttSP/digitalagc.c
index 352ebb2..c9fc7e5 100644
--- a/jDttSP/digitalagc.c
+++ b/jDttSP/digitalagc.c
@@ -36,6 +36,59 @@ Bridgewater, NJ 08807
 
 #include <common.h>
 
+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, hang;
+    REAL peak = 0.0;
+
+    hang = tick < a->over ? a->rcov : a->hang;
+    k = a->indx;
+
+    for (i = 0; i < CXBsize(a->buff); i++)
+      peak = max(peak, Cmag(CXBdata(a->buff, i)));
+
+    if (peak != 0) {
+      a->hist[a->indx] = 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.raw = a->gain.now;
+    a->gain.now = min(a->gain.now, a->gain.top);
+
+    for (i = 0, k = (a->sndx + a->ramp) % a->mask;
+	 i < CXBsize(a->buff);
+	 i++, k = (k + 1) % a->mask)
+      a->circ[k] = CXBdata(a->buff, i);
+
+    if (a->gain.now != a->gain.old) {
+      REAL step = (a->gain.now - a->gain.old) / a->ramp;
+      for (i = 0, k = a->sndx;
+	   i < a->ramp;
+	   i++, k = (k + 1) % a->mask)
+	CXBdata(a->buff, i) = Cscl(a->circ[k], a->gain.old + i * step);
+      for (; i < CXBsize(a->buff); i++, k = (k + 1) % a->mask)
+	CXBdata(a->buff, i) = Cscl(a->circ[k], a->gain.now);
+      a->gain.old = a->gain.now;
+
+    } else {
+      for (i = 0, k = a->sndx;
+	   i < CXBsize(a->buff);
+	   i++, k = (k + 1) % a->mask)
+	CXBdata(a->buff, i) = Cscl(a->circ[k], a->gain.now);
+    }
+
+    a->sndx = (a->sndx + CXBsize(a->buff)) % a->mask;
+    a->indx = ((a->indx + 1) % hang);
+  }
+}
+
 /*
  * Mode is gross agc mode: off, slow, med, fast; just info
  * Hang is basic hang time
@@ -48,22 +101,17 @@ Bridgewater, NJ 08807
 DIGITALAGC
 newDigitalAgc(int Mode,
 	      int Hang,
-	      int Size,
+	      int Ramp,
 	      int Over,
 	      int Rcov,
-	      int Ramp,
 	      int BufSize,
-	      REAL MaxGain,
-	      REAL Limit,
-	      REAL CurGain,
-	      COMPLEX *Vec) {
+	      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;
@@ -71,9 +119,12 @@ newDigitalAgc(int Mode,
   a->gain.lim = Limit;
   a->gain.old = a->gain.now = CurGain;
   a->buff = newCXB(BufSize, Vec, "agc buffer");
+  a->mask = 2 * CXBsize(a->buff);
+  a->circ = newvec_COMPLEX(2 * BufSize, "circular agc buffer");
   memset((void *) a->hist, 0, sizeof(a->hist));
   a->indx = 0;
-  a->gain.fix = 1000.0;
+  a->sndx = a->mask - Ramp;
+  a->gain.fix = 10.0;
   return a;
 }
 
@@ -81,47 +132,7 @@ void
 delDigitalAgc(DIGITALAGC a) {
   if (a) {
     delCXB(a->buff);
+    delvec_COMPLEX(a->circ);
     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 = max(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
index ae886a7..5a6fea1 100644
--- a/jDttSP/digitalagc.h
+++ b/jDttSP/digitalagc.h
@@ -46,12 +46,13 @@ typedef enum _agcmode { agcOFF, agcLONG, agcSLOW, agcMED, agcFAST } AGCMODE;
 typedef
 struct _digitalagc {
   AGCMODE mode;
-  int hang, indx, over, ramp, rcov, size;
+  int hang, indx, over, ramp, rcov, mask, sndx;
   struct {
-    REAL fix, lim, now, old, top;
+    REAL fix, lim, now, old, raw, top;
   } gain;
   REAL hist[AGCHIST];
   CXB buff;
+  COMPLEX *circ;
 } digital_agc_state, *DIGITALAGC;
 
 extern void delDigitalAgc(DIGITALAGC agc);
@@ -59,7 +60,6 @@ extern void delDigitalAgc(DIGITALAGC agc);
 extern DIGITALAGC 
 newDigitalAgc(int Mode,
 	      int Hang,
-	      int Size,
 	      int Ramp,
 	      int Over,
 	      int Rcov,
diff --git a/jDttSP/enums.m4 b/jDttSP/enums.m4
index f9669f7..35133e7 100644
--- a/jDttSP/enums.m4
+++ b/jDttSP/enums.m4
@@ -62,3 +62,26 @@ define(RUN_MUTE,0)dnl
 define(RUN_PASS,1)dnl
 define(RUN_PLAY,2)dnl
 define(RUN_SWCH,3)dnl
+dnl Metering
+define(SIG,0)dnl
+define(AVG,1)dnl
+define(REAL,2)dnl
+define(IMAG,3)dnl
+define(GAIN,4)dnl
+define(ALC,5)dnl
+define(PWR,6)dnl
+define(PKPWR,7)dnl
+dnl Spectrum
+define(SEMI_RAW,0)dnl
+define(PRE_CONV,0)dnl
+define(PRE_FILT,1)dnl
+define(POST_FILT,2)dnl
+define(POST_AGC,3)dnl
+define(POST_MOD,0)dnl
+define(MAG,0)dnl
+define(PWR,1)dnl
+dnl
+define(DCB_LOW,0)dnl
+define(DCB_MED,1)dnl
+define(DCB_HIGH,2)dnl
+define(DCB_SUPER,3)dnl
\ No newline at end of file
diff --git a/jDttSP/keyer.c b/jDttSP/keyer.c
index 581e51e..031b5f0 100644
--- a/jDttSP/keyer.c
+++ b/jDttSP/keyer.c
@@ -201,116 +201,6 @@ klogic(KeyerLogic kl,
   return kl->timeout.beep > 0 && kl->timeout.dlay <= 0;
 }
 
-//========================================================================
-
-/* Read a straight key connected to a serial port, do debouncing, then
-   return the key state */
-
-BOOLEAN
-read_straight_key_serial(KeyerState ks, int fd) {
-  int i, j, serstatus;
-  static BOOLEAN keystate = 0;
-  static int debounce_buf_i = 0,
-             debounce_buf[DEBOUNCE_BUF_MAX_SIZE];
-
-  //***************************************************
-  // replace this with parallel port logic if necessary
-  //***************************************************
-  //
-  /* Read the key state */
-  if (ioctl(fd, TIOCMGET, &serstatus) != -1) {
-    debounce_buf[debounce_buf_i] =
-      (serstatus & (TIOCM_DSR | TIOCM_CTS)) ?
-      DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY;
-
-    debounce_buf_i++;
-  }
-  //
-  //***************************************************
-  // back to business as usual
-  //***************************************************
-
-  /* If the debounce buffer is full, determine the state of the key */
-  if (debounce_buf_i >= ks->debounce) {
-    debounce_buf_i = 0;
-
-    j = 0;
-    for (i = 0; i < ks->debounce; i++)
-      if (debounce_buf[i])
-	j++;
-    keystate = (j > ks->debounce / 2) ? 1 : 0;
-  }
-
-  return keystate;
-}
-
-//------------------------------------------------------------------------
-
-/* Read an iambic key connected to a serial port, do debouncing, emulate a
-   straight key, then return the emulated key state */
-
-BOOLEAN
-read_iambic_key_serial(KeyerState ks, int fd, KeyerLogic kl, double ticklen) {
-  int i, j, serstatus;
-  static BOOLEAN dah_debounce_buf[DEBOUNCE_BUF_MAX_SIZE],
-                 dit_debounce_buf[DEBOUNCE_BUF_MAX_SIZE];
-  static int dah = 0, debounce_buf_i = 0, dit = 0;
-
-  //***************************************************
-  // replace this with parallel port logic if necessary
-  //***************************************************
-  //
-  /* Read the key states */
-  if (ioctl(fd, TIOCMGET, &serstatus) != -1) {
-    if (ks->flag.revpdl) {
-      dah_debounce_buf[debounce_buf_i] =
-	(serstatus & TIOCM_DSR) ? DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY;
-      dit_debounce_buf[debounce_buf_i] =
-	(serstatus & TIOCM_CTS) ? CTS_LINE_CLOSED_KEY : !CTS_LINE_CLOSED_KEY;
-    } else {
-      dit_debounce_buf[debounce_buf_i] =
-	(serstatus & TIOCM_DSR) ? DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY;
-      dah_debounce_buf[debounce_buf_i] =
-	(serstatus & TIOCM_CTS) ? CTS_LINE_CLOSED_KEY : !CTS_LINE_CLOSED_KEY;
-    }
-
-    debounce_buf_i++;
-  }
-  //
-  //***************************************************
-  // back to business as usual
-  //***************************************************
-
-  /* If the debounce buffer is full, determine the state of the keys */
-  if (debounce_buf_i >= ks->debounce) {
-    debounce_buf_i = 0;
-
-    j = 0;
-    for (i = 0; i < ks->debounce; i++)
-      if (dah_debounce_buf[i]) j++;
-    dah = (j > ks->debounce / 2) ? 1 : 0;
-
-    j = 0;
-    for (i = 0; i < ks->debounce; i++)
-      if (dit_debounce_buf[i]) j++;
-    dit = (j > ks->debounce / 2) ? 1 : 0;
-  }
-
-  return klogic(kl,
-		dit,
-		dah,
-		ks->wpm,
-		ks->mode,
-		ks->flag.mdlmdB,
-		ks->flag.memory.dit,
-		ks->flag.memory.dah,
-		ks->flag.autospace.khar,
-		ks->flag.autospace.word,
-		ks->weight,
-		ticklen);
-}
-
-//========================================================================
 
 KeyerState
 newKeyerState(void) {
diff --git a/jDttSP/local.h b/jDttSP/local.h
index 724a8f6..f2f4d2a 100644
--- a/jDttSP/local.h
+++ b/jDttSP/local.h
@@ -61,7 +61,7 @@ extern struct _loc {
   } path;
   struct {
     REAL rate;
-    int size, nrx, spec;
+    int size, nrx, spec, comp;
     SDRMODE mode;
   } def;
   struct { int ring; } mult;
diff --git a/jDttSP/main.c b/jDttSP/main.c
index 7575afb..b288112 100644
--- a/jDttSP/main.c
+++ b/jDttSP/main.c
@@ -48,6 +48,7 @@ extern void reset_counters(void);
 extern void process_samples(float *, float *, float *, float *, int);
 extern void setup_workspace(void);
 extern void destroy_workspace(void);
+extern void clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes);
 
 //========================================================================
 
@@ -71,6 +72,12 @@ spectrum_thread(void) {
       exit(1);
     }
 
+    if (fwrite((char *) uni.spec.oscope, sizeof(float), uni.spec.size, top.meas.spec.fp)
+	!= uni.spec.size) {
+      fprintf(stderr, "error writing oscope\n");
+      exit(1);
+    }
+
     fflush(top.meas.spec.fp);
   }
 
@@ -246,6 +253,8 @@ run_swch(void) {
 
     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);
 
     reset_meters();
     reset_spectrum();
@@ -576,6 +585,7 @@ setup_defaults(void) {
   loc.def.size = DEFSIZE;
   loc.def.mode = DEFMODE;
   loc.def.spec = DEFSPEC;
+  loc.def.comp = DEFCOMP;
   loc.def.nrx = MAXRX;
   loc.mult.ring = RINGMULT;
 
diff --git a/jDttSP/meter.c b/jDttSP/meter.c
index d27e80d..ee15fd1 100644
--- a/jDttSP/meter.c
+++ b/jDttSP/meter.c
@@ -44,4 +44,3 @@ snap_meter(MeterBlock *mb, int label) {
 	 TXMETERPTS * sizeof(REAL));
   mb->label = label;
 }
-
diff --git a/jDttSP/meter.h b/jDttSP/meter.h
index 3deb654..f02cdb8 100644
--- a/jDttSP/meter.h
+++ b/jDttSP/meter.h
@@ -62,7 +62,11 @@ typedef enum {
   SIGNAL_STRENGTH, 
   AVG_SIGNAL_STRENGTH, 
   ADC_REAL, 
-  ADC_IMAG
+  ADC_IMAG,
+  AGC_GAIN,
+  ALC,  
+  PWR,
+  PKPWR
 } METERTYPE;
 
 #define RXMETERPTS (3)
diff --git a/jDttSP/mkchan.c b/jDttSP/mkchan.c
deleted file mode 100644
index 2855980..0000000
--- a/jDttSP/mkchan.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* mkchan.c */
-/* create a file big enough to accommodate
-   a header + a ringbuffer of a given size */
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <stdlib.h>
-#include <values.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <math.h>
-#include <assert.h>
-#include <libgen.h>
-
-#include <datatypes.h>
-#include <banal.h>
-#include <ringb.h>
-
-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/ringb.c b/jDttSP/ringb.c
index 1e6c31e..8a1cfaf 100644
--- a/jDttSP/ringb.c
+++ b/jDttSP/ringb.c
@@ -45,6 +45,20 @@ ringb_reset(ringb_t *rb) {
   rb->wptr = 0;
 }
 
+void
+ringb_clear(ringb_t *rb, size_t nbytes) {
+  size_t i;
+  char zero = 0;
+  for (i = 0; i < nbytes; i++)
+    ringb_write(rb, &zero, 1);
+}
+
+void
+ringb_restart(ringb_t *rb, size_t nbytes) {
+  ringb_reset(rb);
+  ringb_clear(rb, nbytes);
+}
+
 size_t
 ringb_read_space(const ringb_t *rb) {
   size_t w = rb->wptr, r = rb->rptr;
diff --git a/jDttSP/ringb.h b/jDttSP/ringb.h
index c92fd3c..55f4a2e 100644
--- a/jDttSP/ringb.h
+++ b/jDttSP/ringb.h
@@ -169,4 +169,18 @@ extern void ringb_write_advance(ringb_t *rb, size_t cnt);
 
 extern size_t ringb_write_space(const ringb_t *rb);
 
+/* Fill the ring buffer for nbytes at the beginning with zeros 
+ * rb a pointer to the ring buffer structure
+ * nbytes the number of bytes to be written */
+
+extern void ringb_clear(ringb_t *rb, size_t nbytes);
+
+/* Reset the read and write pointers, making an empty buffer.
+ * This is not thread safe. 
+ * Fill the ring buffer for nbytes at the beginning with zeros 
+ * rb a pointer to the ring buffer structure
+ * nbytes the number of bytes to be written */
+
+extern void ringb_restart(ringb_t *rb, size_t nbytes);
+
 #endif
diff --git a/jDttSP/sdr.c b/jDttSP/sdr.c
index aad64d3..9374813 100644
--- a/jDttSP/sdr.c
+++ b/jDttSP/sdr.c
@@ -115,6 +115,8 @@ setup_all(void) {
   uni.mix.rx.flag = uni.mix.tx.flag = FALSE;
   uni.mix.rx.gain = uni.mix.tx.gain = 1.0;
   
+  uni.cpdlen = loc.def.comp;
+
   uni.tick = 0;
 }
 
@@ -146,7 +148,7 @@ setup_rx(int k) {
      we just created */
   rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv),
 		       FiltOvSv_fetchpoint(rx[k].filt.ovsv),
-		       "init rx.buf.i");
+		       "init rx[k].buf.i");
   rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv),
 		       FiltOvSv_storepoint(rx[k].filt.ovsv),
 		       "init rx[k].buf.o");
@@ -161,17 +163,16 @@ setup_rx(int k) {
 			 uni.samplerate,
 			 "SDR RX Oscillator");
 
-  rx[k].agc.gen = newDigitalAgc(agcMED,	// Mode
-			     7,		// Hang
-			     7,		// Size
-			     48,	// Ramp
-			     3,		// Over
-			     3,		// Rcov
-			     CXBsize(rx[k].buf.o),	// BufSize
-			     100.0,	// MaxGain
-			     0.707,	// Limit
-			     1.0,	// CurGain
-			     CXBbase(rx[k].buf.o));
+  rx[k].agc.gen = newDigitalAgc(agcSLOW,	// Mode
+				7,		// Hang
+				48,		// Ramp
+				3,		// Over
+				3,		// Rcov
+				CXBsize(rx[k].buf.o),	// BufSize
+				2500.0,		// MaxGain
+				0.707,		// Limit
+				1.0,		// CurGain
+				CXBbase(rx[k].buf.o));
   rx[k].agc.flag = TRUE;
 
   /* demods */
@@ -236,7 +237,12 @@ setup_rx(int k) {
   rx[k].squelch.thresh = -30.0;
   rx[k].squelch.power = 0.0;
   rx[k].squelch.flag = rx[k].squelch.running = rx[k].squelch.set = FALSE;
-  rx[k].squelch.num = (int) (0.0395 * uni.samplerate + 0.5);
+  rx[k].squelch.num = uni.buflen - 10;
+
+  rx[k].cpd.gen = newWSCompander(uni.cpdlen,
+				 0.0,
+				 rx[k].buf.o);
+  rx[k].cpd.flag = FALSE;
 
   rx[k].mode = uni.mode.sdr;
   rx[k].bin.flag = FALSE;
@@ -281,6 +287,9 @@ setup_tx(void) {
 		    FiltOvSv_storepoint(tx.filt.ovsv),
 		    "init tx.buf.o");
   
+  tx.dcb.flag = FALSE;
+  tx.dcb.gen = newDCBlocker(DCB_MED, tx.buf.i);
+
   /* conversion */
   tx.osc.freq = 0.0;
   tx.osc.phase = 0.0;
@@ -291,12 +300,12 @@ setup_tx(void) {
 		      uni.samplerate,
 		      "SDR TX Oscillator");
 
+
   tx.agc.gen = newDigitalAgc(agcFAST, 	// Mode
 			     3,		// Hang
-			     3,		// Size
+			     48,	// Ramp
 			     3,		// Over
 			     3,		// Rcov
-			     48,	// Ramp
 			     CXBsize(tx.buf.o),	// BufSize
 			     1.0,	// MaxGain
 			     0.900,	// Limit
@@ -304,9 +313,14 @@ setup_tx(void) {
 			     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.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.o), CXBsize(tx.buf.o));
   tx.spr.flag = FALSE;
 
+  tx.cpd.gen = newWSCompander(uni.cpdlen,
+			      0.0,
+			      tx.buf.o);
+  tx.cpd.flag = FALSE;
+
   tx.scl.dc = cxzero;
   tx.scl.pre.val = 1.0;
   tx.scl.pre.flag = FALSE;
@@ -345,6 +359,7 @@ destroy_workspace(void) {
   delSpeechProc(tx.spr.gen);
   delDigitalAgc(tx.agc.gen);
   delOSC(tx.osc.gen);
+  delDCBlocker(tx.dcb.gen);
   delvec_COMPLEX(tx.filt.save);
   delFiltOvSv(tx.filt.ovsv);
   delFIR_Bandpass_COMPLEX(tx.filt.coef);
@@ -405,20 +420,21 @@ do_rx_meter(int k, CXB buf, int tap) {
   uni.meter.rx.val[k][tap] = 0;
   
   switch (uni.meter.rx.type) {
-  case AVG_SIGNAL_STRENGTH:
-    for (i = 0; i < len; i++)
-      uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
-    uni.meter.rx.val[k][tap] =
-      uni.meter.rx.avg[k][tap] =
-        0.9 * uni.meter.rx.avg[k][tap] + log10(uni.meter.rx.val[k][tap] + 1e-20);
-    break;
   case SIGNAL_STRENGTH:
     for (i = 0; i < len; i++)
       uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
+    if (tap == 3) rx[k].norm = uni.meter.rx.val[k][tap] / len;
     uni.meter.rx.avg[k][tap] =
       uni.meter.rx.val[k][tap] =
         10.0 * log10(uni.meter.rx.val[k][tap] + 1e-20);
     break;
+  case AVG_SIGNAL_STRENGTH:
+    for (i = 0; i < len; i++)
+      uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
+    uni.meter.rx.val[k][tap] =
+      uni.meter.rx.avg[k][tap] =
+        0.9 * uni.meter.rx.avg[k][tap] + log10(uni.meter.rx.val[k][tap] + 1e-20);
+    break;
   case ADC_REAL:
     for(i = 0; i < len; i++)
       uni.meter.rx.val[k][tap] = max(fabs(vec[i].re), uni.meter.rx.val[k][tap]);
@@ -429,6 +445,9 @@ do_rx_meter(int k, CXB buf, int tap) {
       uni.meter.rx.val[k][tap] = max(fabs(vec[i].im), uni.meter.rx.val[k][tap]);
     uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10);
     break;
+  case AGC_GAIN:
+    uni.meter.rx.val[k][tap] = 20.0 * log10(rx[k].agc.gen->gain.now + 1e-80);
+    break;
   default:
     break;
   }
@@ -456,28 +475,35 @@ do_tx_meter(CXB buf, int tap) {
       uni.meter.tx.val[tap] =
         10.0 * log10(uni.meter.tx.val[tap] + 1e-20);
     break;
-  case ADC_REAL:
-    for(i = 0; i < len; i++)
-      uni.meter.tx.val[tap] = max(fabs(vec[i].re), uni.meter.tx.val[tap]);
-    uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10);
+  case ALC:
+    {
+      REAL tmp = 20.0 * log10(tx.agc.gen->gain.now);
+      uni.meter.tx.val[tap] =
+	tmp < 0.0 ? tmp : min(20.0, 20.0 * log10(tx.agc.gen->gain.raw));
+    }
     break;
-  case ADC_IMAG:
-    for(i = 0; i < len; i++)
-      uni.meter.tx.val[tap] = max(fabs(vec[i].im), uni.meter.tx.val[tap]);
-    uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10);
+  case PWR:
+    for(i = 0, uni.meter.tx.val[tap] = 1e-20; i < CXBhave(tx.buf.o); i++)
+      uni.meter.tx.val[tap] += Csqrmag(CXBdata(tx.buf.o, i));
+    uni.meter.tx.val[tap] /= 2048.0;
+    break;
+  case PKPWR:
+    for(i = 0, uni.meter.tx.val[tap] = 1e-20; i < CXBhave(tx.buf.o); i++) 
+      uni.meter.tx.val[tap] = max(uni.meter.tx.val[tap],
+				  Csqrmag(CXBdata(tx.buf.o,i)));
     break;
   default:
     break;
   }
 }
 
-PRIVATE BOOLEAN
+PRIVATE void
 do_rx_spectrum(int k, CXB buf, int type) {
   if (uni.spec.flag && k == uni.spec.rxk && type == uni.spec.type) {
     memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
 	   (char *) CXBbase(buf),
-	   CXBhave(buf)); 
-    uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size;
+	   CXBsize(buf) * sizeof(COMPLEX)); 
+    uni.spec.fill = (uni.spec.fill + CXBsize(buf)) % uni.spec.size;
   }
 }
 
@@ -485,8 +511,8 @@ PRIVATE void
 do_tx_spectrum(CXB buf) {
   memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
 	 (char *) CXBbase(buf),
-	 CXBhave(buf));
-  uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size;
+	 CXBsize(buf) * sizeof(COMPLEX));
+  uni.spec.fill = (uni.spec.fill + CXBsize(buf)) % uni.spec.size;
 }
 
 //========================================================================
@@ -497,9 +523,13 @@ should_do_rx_squelch(int k) {
   if (rx[k].squelch.flag) {
     int i, n = CXBhave(rx[k].buf.o);
     rx[k].squelch.power = 0.0;
+
     for (i = 0; i < n; i++)
       rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i));
-    return rx[k].squelch.thresh > 10.0 * log10(rx[k].squelch.power);
+
+    return
+      10.0 * log10(rx[k].squelch.power) < rx[k].squelch.thresh;
+
   } else
     return rx[k].squelch.set = FALSE;
 }
@@ -510,14 +540,25 @@ should_do_rx_squelch(int k) {
 PRIVATE void
 do_squelch(int k) {
   rx[k].squelch.set = TRUE;
+  
   if (!rx[k].squelch.running) {
-    int i, m = rx[k].squelch.num, n = CXBhave(rx[k].buf.o) - m;
+    int i,
+        m = rx[k].squelch.num,
+        n = CXBhave(rx[k].buf.o) - m;
+
     for (i = 0; i < m; i++)
-      CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m);
-    memset((void *) (CXBbase(rx[k].buf.o) + m), 0, n * sizeof(COMPLEX));
+      CXBdata(rx[k].buf.o, i) =
+	Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m);
+  
+    memset((void *) (CXBbase(rx[k].buf.o) + m),
+	   0,
+	   n * sizeof(COMPLEX));
     rx[k].squelch.running = TRUE;
+
   } else
-    memset((void *) CXBbase(rx[k].buf.o), 0, CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
+    memset((void *) CXBbase(rx[k].buf.o),
+	   0,
+	   CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
 }
 
 // lift squelch
@@ -527,8 +568,11 @@ PRIVATE void
 no_squelch(int k) {
   if (rx[k].squelch.running) {
     int i, m = rx[k].squelch.num;
+
     for (i = 0; i < m; i++)
-      CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m);
+      CXBdata(rx[k].buf.o, i) =
+	Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m);
+
     rx[k].squelch.running = FALSE;
   }
 }
@@ -582,12 +626,16 @@ do_rx_pre(int k) {
     do_rx_meter(k, rx[k].buf.o, RXMETER_POST_FILT);
     do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_FILT);
     
+    if (rx[k].cpd.flag)
+      WSCompand(rx[k].cpd.gen);
+
     if (should_do_rx_squelch(k))
       do_squelch(k);
     
     else if (rx[k].agc.flag)
       DigitalAgc(rx[k].agc.gen, rx[k].tick);
-    
+
+    do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_AGC);
   }
 }
 
@@ -682,18 +730,19 @@ do_rx(int k) {
 PRIVATE void
 do_tx_pre(void) {
 
-if (tx.scl.pre.flag) {
-int i, n = CXBhave(tx.buf.i);
+  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);
   }
 
   correctIQ(tx.buf.i, tx.iqfix);
 
-  if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
+  if (tx.dcb.flag) DCBlock(tx.dcb.gen);
 
   if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
   filter_OvSv(tx.filt.ovsv);
+
 }
 
 PRIVATE void
@@ -712,6 +761,9 @@ do_tx_post(void) {
       CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
   }
 
+  if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
+  if (tx.cpd.flag) WSCompand(tx.cpd.gen);
+
   if (uni.spec.flag)
     do_tx_spectrum(tx.buf.o);
 
@@ -825,16 +877,16 @@ process_samples(float *bufl, float *bufr,
 	do_rx(k), rx[k].tick++;
 	// mix
 	for (i = 0; i < n; i++)
-          bufl[i] += CXBimag(rx[k].buf.o, i),
-	  bufr[i] += CXBreal(rx[k].buf.o, i);
+          bufl[i] += (float) CXBimag(rx[k].buf.o, i),
+	  bufr[i] += (float) CXBreal(rx[k].buf.o, i);
 	CXBhave(rx[k].buf.o) = n;
       }
 
     // late mixing of aux buffers
     if (uni.mix.rx.flag)
       for (i = 0; i < n; i++)
-	bufl[i] += auxl[i] * uni.mix.rx.gain,
-	bufr[i] += auxr[i] * uni.mix.rx.gain;
+	bufl[i] += (float) (auxl[i] * uni.mix.rx.gain),
+	bufr[i] += (float) (auxr[i] * uni.mix.rx.gain);
 
     break;
 
@@ -843,8 +895,8 @@ process_samples(float *bufl, float *bufr,
     // early mixing of aux buffers
     if (uni.mix.tx.flag)
       for (i = 0; i < n; i++)
-	bufl[i] += auxl[i] * uni.mix.tx.gain,
-	bufr[i] += auxr[i] * uni.mix.tx.gain;
+	bufl[i] += (float) (auxl[i] * uni.mix.tx.gain),
+	bufr[i] += (float) (auxr[i] * uni.mix.tx.gain);
 
     for (i = 0; i < n; i++)
       CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
diff --git a/jDttSP/sdrexport.h b/jDttSP/sdrexport.h
index 6303a65..68de488 100644
--- a/jDttSP/sdrexport.h
+++ b/jDttSP/sdrexport.h
@@ -60,13 +60,12 @@ Bridgewater, NJ 08807
 #include <local.h>
 #include <meter.h>
 #include <spectrum.h>
+
 //------------------------------------------------------------------------
 // max no. simultaneous receivers
 #ifndef MAXRX
 #define MAXRX (4)
 #endif
-//------------------------------------------------------------------------
-/* modulation types, modes */ 
 
 //========================================================================
 /* RX/TX both */ 
@@ -110,6 +109,8 @@ extern struct _uni {
     } rx, tx;
   } mix;
 
+  int cpdlen;
+
   long tick;
   
 } uni;
@@ -167,6 +168,12 @@ extern struct _rx {
     BOOLEAN flag, running, set;
     int num;
   } squelch;
+
+  struct {
+    BOOLEAN flag;
+    WSCompander gen;
+  } cpd;
+
   SDRMODE mode;
   struct { BOOLEAN flag; } bin;
   REAL norm;
@@ -178,32 +185,50 @@ extern struct _rx {
 /* TX */ 
 //------------------------------------------------------------------------
 extern struct _tx {
+
   struct {
     CXB i, o;
   } buf;
+
   IQ iqfix;
+
+  struct {
+    BOOLEAN flag;
+    DCBlocker gen;
+  } dcb;
+
   struct {
     REAL freq, phase;
     OSC gen;
   } osc;
+
   struct {
     ComplexFIR coef;
     FiltOvSv ovsv;
     COMPLEX *save;
   } filt;
+
+  struct {
+    SpeechProc gen;
+    BOOLEAN flag;
+  } spr;
+
+  struct {
+    BOOLEAN flag;
+    WSCompander gen;
+  } cpd;
+
   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 {
@@ -211,6 +236,7 @@ extern struct _tx {
       BOOLEAN flag;
     } pre, post;
   } scl;
+
   SDRMODE mode;
   long tick;
   REAL norm;
@@ -320,6 +346,7 @@ extern struct _top {
     } run;
     int fade, tail;
   } swch;
+
 } top;
 
 #endif
diff --git a/jDttSP/spectrum.c b/jDttSP/spectrum.c
index 441f92e..e547567 100644
--- a/jDttSP/spectrum.c
+++ b/jDttSP/spectrum.c
@@ -39,12 +39,14 @@ snap_spectrum(SpecBlock *sb, int label) {
   int i, j;
 
   // where most recent signal started
-  j = (sb->fill - sb->buflen + sb->size) % sb->size;
+  j = sb->fill;
 
   // copy starting from there in circular fashion,
   // applying window as we go
   for (i = 0; i < sb->size; i++) {
-    CXBdata(sb->timebuf, i) = Cscl(CXBdata(sb->timebuf, j), sb->window[i]);
+    COMPLEX z = CXBdata(sb->accum, j);
+    sb->oscope[i] = (float) z.re;
+    CXBdata(sb->timebuf, i) = Cscl(z, sb->window[i]);
     j = ++j % sb->size;
   }
   
@@ -62,18 +64,17 @@ compute_spectrum(SpecBlock *sb) {
 	   (fftw_complex *) CXBbase(sb->timebuf),
 	   (fftw_complex *) CXBbase(sb->freqbuf));
 
-  if (sb->scale == SPEC_MAG) {
-    for (i = 0; i < half; i++)
-      sb->output[i + half] = Cmag(CXBdata(sb->freqbuf, i));
-    for (; i < sb->size; i++)
-      sb->output[i] = Cmag(CXBdata(sb->freqbuf, i));
-
-  } else { // SPEC_PWR
-    for (i = 0; i < half; i++)
-      sb->output[i + half] = 10.0 * log10(Csqrmag(CXBdata(sb->freqbuf, i)) + 1e-60);
-    for (; i < sb->size; i++)
-      sb->output[i] = 10.0 * log10(Csqrmag(CXBdata(sb->freqbuf, i)) + 1e-60);
-  }
+  if (sb->scale == SPEC_MAG)
+
+    for (i = 0; i < sb->size; i++)
+      sb->output[i] =
+	(float) Cmag(CXBdata(sb->freqbuf, (i + half) % sb->size));
+
+  else	// SPEC_PWR
+
+    for (i = 0; i < sb->size; i++)
+      sb->output[i] =
+	(float) (10.0 * log10(Csqrmag(CXBdata(sb->freqbuf, (i + half) % sb->size)) + 1e-60));
 }
 
 void
@@ -84,6 +85,7 @@ init_spectrum(SpecBlock *sb) {
   sb->freqbuf = newCXB(sb->size, 0, "spectrum freqbuf");
   sb->window = newvec_REAL(sb->size, "spectrum window");
   sb->output = (float *) safealloc(sb->size, sizeof(float), "spectrum output");
+  sb->oscope = (float *) safealloc(sb->size, sizeof(float), "spectrum oscope");
   sb->plan = fftw_create_plan(sb->size, FFTW_FORWARD, sb->planbits);
 }
 
@@ -92,6 +94,7 @@ reinit_spectrum(SpecBlock *sb) {
   sb->fill = sb->size - sb->buflen;
   memset((char *) CXBbase(sb->accum), 0, sb->size * sizeof(REAL));
   memset((char *) sb->output, 0, sb->size * sizeof(float));
+  memset((char *) sb->oscope, 0, sb->size * sizeof(float));
 }
 
 void
@@ -102,6 +105,7 @@ finish_spectrum(SpecBlock *sb) {
     delCXB(sb->freqbuf);
     delvec_REAL(sb->window);
     safefree((char *) sb->output);
+    safefree((char *) sb->oscope);
     fftw_destroy_plan(sb->plan);
   }
 }
diff --git a/jDttSP/spectrum.h b/jDttSP/spectrum.h
index adef3dc..52575ba 100644
--- a/jDttSP/spectrum.h
+++ b/jDttSP/spectrum.h
@@ -64,6 +64,7 @@ Bridgewater, NJ 08807
 #define SPEC_SEMI_RAW	(0)
 #define SPEC_PRE_FILT	(1)
 #define SPEC_POST_FILT	(2)
+#define SPEC_POST_AGC	(3)
 
 typedef
 struct _spec_block {
@@ -72,7 +73,7 @@ struct _spec_block {
   CXB accum, timebuf, freqbuf;
   int fill, buflen, rxk, scale, size, type;
   REAL *window;
-  float *output;
+  float *output, *oscope;
   int planbits;
   fftw_plan plan;
 } SpecBlock;
diff --git a/jDttSP/update.c b/jDttSP/update.c
index 0e6ef67..ceed465 100644
--- a/jDttSP/update.c
+++ b/jDttSP/update.c
@@ -562,7 +562,7 @@ setcorrectIQgain(int n, char **p) {
 
 PRIVATE int
 setSquelch(int n, char **p) {
-  rx[RL].squelch.thresh = -atof(p[0]);
+  rx[RL].squelch.thresh = atof(p[0]);
   return 0;
 }
 
@@ -816,6 +816,43 @@ setAuxMixGain(int n, char **p) {
   }
 }
 
+PRIVATE int
+setCompandSt(int n, char **p) {
+  if (n < 1) {
+    tx.cpd.flag = FALSE;
+    return 0;
+  } else {
+    BOOLEAN flag = atoi(p[0]);
+    if (n > 1) {
+      switch (atoi(p[1])) {
+      case RX: rx[RL].cpd.flag = flag; break;
+      case TX:
+      default: tx.cpd.flag = flag; break;
+      }
+    } else
+      tx.cpd.flag = flag;
+    return 0;
+  }
+}
+
+PRIVATE int
+setCompand(int n, char **p) {
+  if (n < 1)
+    return -1;
+  else {
+    REAL fac = atof(p[0]);
+    if (n > 1) {
+      switch (atoi(p[1])) {
+      case RX: WSCReset(rx[RL].cpd.gen, fac); break;
+      case TX:
+      default: WSCReset(tx.cpd.gen, fac); break;
+      }
+    } else
+      WSCReset(tx.cpd.gen, fac);
+    return 0;
+  }
+}
+
 //------------------------------------------------------------------------
 
 // [type]
@@ -846,48 +883,41 @@ setSpectrumType(int n, char **p) {
   uni.spec.rxk   = RL;
   switch (n) {
   case 3:
-    uni.spec.rxk   = atoi(p[2]);
+    uni.spec.rxk = atoi(p[2]);
   case 2:
     uni.spec.scale = atoi(p[1]);
+    break;
   case 1:
-    uni.spec.type  = atoi(p[0]);
+    uni.spec.type = atoi(p[0]);
     break;
   case 0:
-    return 0;
+    break;
   default:
     return -1;
   }
+  return uni.spec.type;
 }
 
-#if 0
 PRIVATE int
-setSpectrumType(int n, char **p) {
-  switch (n) {
-  case 3:
-    uni.spec.type = atoi(p[0]);
-    uni.spec.scale = atoi(p[1]);
-    uni.spec.rxk = atoi(p[2]);
-  case 2:
-    uni.spec.type = atoi(p[0]);
-    uni.spec.scale = atoi(p[1]);
-    uni.spec.rxk = RL;
-    break;
-  case 1:
-    uni.spec.type = atoi(p[0]);
-    uni.spec.scale = SPEC_PWR;
-    uni.spec.rxk = RL;
-    break;
-  case 0:
-    uni.spec.type = SPEC_POST_FILT;
-    uni.spec.scale = SPEC_PWR;
-    uni.spec.rxk = RL;
-    break;
-  default:
+setDCBlockSt(int n, char **p) {
+  if (n < 1) {
+    tx.dcb.flag = FALSE;
+    return 0;
+  } else {
+    tx.dcb.flag = atoi(p[0]);
+    return 0;
+  }
+}
+
+PRIVATE int
+setDCBlock(int n, char **p) {
+  if (n < 1)
     return -1;
+  else {
+    resetDCBlocker(tx.dcb.gen, atoi(p[0]));
+    return 0;
   }
-  return 0;
 }
-#endif
 
 //========================================================================
 
@@ -968,8 +998,12 @@ CTE update_cmds[] = {
   {"setRXPan", setRXPan},
   {"setAuxMixSt", setAuxMixSt},
   {"setAuxMixGain", setAuxMixGain},
+  {"setCompandSt", setCompandSt},
+  {"setCompand", setCompand},
   {"setMeterType", setMeterType},
   {"setSpectrumType", setSpectrumType},
+  {"setDCBlockSt", setDCBlockSt},
+  {"setDCBlock", setDCBlock},
   { 0, 0 }
 };
 
diff --git a/jDttSP/wscompand.c b/jDttSP/wscompand.c
new file mode 100644
index 0000000..0931ef7
--- /dev/null
+++ b/jDttSP/wscompand.c
@@ -0,0 +1,108 @@
+// wscompand.c
+// waveshaping compander, mostly for speech
+/*
+This file is part of a program that implements a Software-Defined Radio.
+
+Copyright (C) 2004-2005 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 <wscompand.h>
+
+PRIVATE INLINE REAL
+WSCLookup(WSCompander wsc, REAL x) {
+  if (x > 0.0) {
+    REAL d = x - (int) x, y, *tbl = wsc->tbl;
+    int i = (int) (x * wsc->npts), end = wsc->nend;
+    if (i < end)
+      y = tbl[i] + d * (tbl[i + 1] - tbl[i]);
+    else
+      y = tbl[end];
+    return y / x;
+  } else
+    return 0.0;
+}
+
+void
+WSCompand(WSCompander wsc) {
+  int i, n = CXBsize(wsc->buff);
+  for (i = 0; i < n; i++) {
+    COMPLEX val = CXBdata(wsc->buff, i);
+    REAL mag = Cmag(val),
+         scl = WSCLookup(wsc, mag);
+    CXBdata(wsc->buff, i) = Cscl(val, scl);
+  }
+}
+
+void
+WSCReset(WSCompander wsc, REAL fac) {
+  int i;
+  REAL *tbl = wsc->tbl;
+
+  if (fac == 0.0)	// just linear
+    for (i = 0; i < wsc->npts; i++)
+      tbl[i] = i / (REAL) wsc->npts;
+
+  else {		// exponential
+    REAL del = fac / wsc->nend,
+         scl = 1.0 - exp(fac);
+    for (i = 0; i < wsc->npts; i++)
+      tbl[i] = (1.0 - exp(i * del)) / scl;
+  }
+
+  wsc->fac = fac;
+}
+
+// fac < 0: compression
+// fac > 0: expansion
+
+WSCompander
+newWSCompander(int npts,
+	       REAL fac,
+	       CXB buff) {
+  WSCompander wsc;
+
+  wsc = (WSCompander) safealloc(1,
+				sizeof(WSCompanderInfo),
+				"WSCompander struct");
+  wsc->npts = npts;
+  wsc->nend = npts - 1;
+  wsc->tbl = newvec_REAL(npts, "WSCompander table");
+  wsc->buff = newCXB(npts, CXBbase(buff), "WSCompander buff");
+  WSCReset(wsc, fac);
+  return wsc;
+}
+
+void
+delWSCompander(WSCompander wsc) {
+  if (wsc) {
+    delvec_REAL(wsc->tbl);
+    delCXB(wsc->buff);
+    safefree((char *) wsc);
+  }
+}
diff --git a/jDttSP/wscompand.h b/jDttSP/wscompand.h
new file mode 100644
index 0000000..88d2411
--- /dev/null
+++ b/jDttSP/wscompand.h
@@ -0,0 +1,60 @@
+// wscompand.h
+/*
+This file is part of a program that implements a Software-Defined Radio.
+
+Copyright (C) 2004-2005 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 _wscompand_h
+#define _wscompand_h
+
+#include <fromsys.h>
+#include <defs.h>
+#include <banal.h>
+#include <splitfields.h>
+#include <datatypes.h>
+#include <bufvec.h>
+#include <cxops.h>
+
+typedef struct _wscompander {
+  int npts, nend;
+  REAL fac, *tbl;
+  CXB buff;
+} WSCompanderInfo, *WSCompander;
+
+extern void WSCompand(WSCompander wsc);
+
+// fac < 0: compression
+// fac > 0: expansion
+
+extern void WSCReset(WSCompander wsc, REAL fac);
+extern WSCompander newWSCompander(int npts, REAL fac, CXB buff);
+extern void delWSCompander(WSCompander wsc);
+
+#endif
-- 
2.45.2