-#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
correctIQ.o\
crc16.o\
cxops.o\
+ dcblock.o\
digitalagc.o\
fastrig.o\
filter.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
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
# 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)
+
#define _banal_h
#include <fromsys.h>
+#include <defs.h>
#include <datatypes.h>
#ifndef min
-[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
+//
#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>
--- /dev/null
+// 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];
+}
--- /dev/null
+// 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
#define DEFSIZE (2048)
#define DEFMODE (SAM)
#define DEFSPEC (4096)
+#define DEFCOMP (4096)
#define MAXRX (4)
#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
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;
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;
}
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;
- }
-}
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);
extern DIGITALAGC
newDigitalAgc(int Mode,
int Hang,
- int Size,
int Ramp,
int Over,
int Rcov,
define(RUN_PASS,1)dnl\r
define(RUN_PLAY,2)dnl\r
define(RUN_SWCH,3)dnl\r
+dnl Metering\r
+define(SIG,0)dnl\r
+define(AVG,1)dnl\r
+define(REAL,2)dnl\r
+define(IMAG,3)dnl\r
+define(GAIN,4)dnl\r
+define(ALC,5)dnl\r
+define(PWR,6)dnl\r
+define(PKPWR,7)dnl\r
+dnl Spectrum\r
+define(SEMI_RAW,0)dnl\r
+define(PRE_CONV,0)dnl\r
+define(PRE_FILT,1)dnl\r
+define(POST_FILT,2)dnl\r
+define(POST_AGC,3)dnl\r
+define(POST_MOD,0)dnl\r
+define(MAG,0)dnl\r
+define(PWR,1)dnl\r
+dnl\r
+define(DCB_LOW,0)dnl\r
+define(DCB_MED,1)dnl\r
+define(DCB_HIGH,2)dnl\r
+define(DCB_SUPER,3)dnl
\ No newline at end of file
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) {
} path;
struct {
REAL rate;
- int size, nrx, spec;
+ int size, nrx, spec, comp;
SDRMODE mode;
} def;
struct { int ring; } mult;
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);
//========================================================================
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);
}
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();
loc.def.size = DEFSIZE;
loc.def.mode = DEFMODE;
loc.def.spec = DEFSPEC;
+ loc.def.comp = DEFCOMP;
loc.def.nrx = MAXRX;
loc.mult.ring = RINGMULT;
TXMETERPTS * sizeof(REAL));
mb->label = label;
}
-
SIGNAL_STRENGTH,
AVG_SIGNAL_STRENGTH,
ADC_REAL,
- ADC_IMAG
+ ADC_IMAG,
+ AGC_GAIN,
+ ALC,
+ PWR,
+ PKPWR
} METERTYPE;
#define RXMETERPTS (3)
+++ /dev/null
-/* 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(); }
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;
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
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;
}
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");
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 */
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;
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;
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
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;
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);
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]);
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;
}
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;
}
}
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;
}
//========================================================================
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;
}
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
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;
}
}
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);
}
}
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
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);
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;
// 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];
#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 */
} rx, tx;
} mix;
+ int cpdlen;
+
long tick;
} uni;
BOOLEAN flag, running, set;
int num;
} squelch;
+
+ struct {
+ BOOLEAN flag;
+ WSCompander gen;
+ } cpd;
+
SDRMODE mode;
struct { BOOLEAN flag; } bin;
REAL norm;
/* 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 {
BOOLEAN flag;
} pre, post;
} scl;
+
SDRMODE mode;
long tick;
REAL norm;
} run;
int fade, tail;
} swch;
+
} top;
#endif
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;
}
(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
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);
}
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
delCXB(sb->freqbuf);
delvec_REAL(sb->window);
safefree((char *) sb->output);
+ safefree((char *) sb->oscope);
fftw_destroy_plan(sb->plan);
}
}
#define SPEC_SEMI_RAW (0)
#define SPEC_PRE_FILT (1)
#define SPEC_POST_FILT (2)
+#define SPEC_POST_AGC (3)
typedef
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;
PRIVATE int
setSquelch(int n, char **p) {
- rx[RL].squelch.thresh = -atof(p[0]);
+ rx[RL].squelch.thresh = atof(p[0]);
return 0;
}
}
}
+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]
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
//========================================================================
{"setRXPan", setRXPan},
{"setAuxMixSt", setAuxMixSt},
{"setAuxMixGain", setAuxMixGain},
+ {"setCompandSt", setCompandSt},
+ {"setCompand", setCompand},
{"setMeterType", setMeterType},
{"setSpectrumType", setSpectrumType},
+ {"setDCBlockSt", setDCBlockSt},
+ {"setDCBlock", setDCBlock},
{ 0, 0 }
};
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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