From 00027b8895e28eedb988f3f1a67679a3b3dfe770 Mon Sep 17 00:00:00 2001 From: dttsp Date: Fri, 29 Apr 2005 22:10:59 +0000 Subject: [PATCH] multiple receivers implemented --- jDttSP/Makefile | 2 +- jDttSP/command-vocabulary | 6 +- jDttSP/fastrig.c | 2 +- jDttSP/keyd.c | 2 - jDttSP/local.h | 6 +- jDttSP/main.c | 11 +- jDttSP/sdr.c | 467 +++++++++++++++++++++----------------- jDttSP/sdrexport.c | 2 +- jDttSP/sdrexport.h | 13 +- jDttSP/update.c | 256 ++++++++++++++------- 10 files changed, 472 insertions(+), 295 deletions(-) diff --git a/jDttSP/Makefile b/jDttSP/Makefile index b13744d..f2e978e 100644 --- a/jDttSP/Makefile +++ b/jDttSP/Makefile @@ -59,7 +59,7 @@ ipc: mkchan obj: $(OBJ) clean: - /bin/rm *.o jsdr mkchan metermon keyd keyb #$(staticlibname) + /bin/rm *.o jsdr mkchan metermon keyd #$(staticlibname) #/bin/rm IPC/* staticlib: $(OBJ) diff --git a/jDttSP/command-vocabulary b/jDttSP/command-vocabulary index 3f479be..2655e4c 100644 --- a/jDttSP/command-vocabulary +++ b/jDttSP/command-vocabulary @@ -50,4 +50,8 @@ 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 \ No newline at end of file +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) diff --git a/jDttSP/fastrig.c b/jDttSP/fastrig.c index 52c77b1..1ea11a2 100644 --- a/jDttSP/fastrig.c +++ b/jDttSP/fastrig.c @@ -1,6 +1,6 @@ /**************************************************************** - * Fast Trigonometric Routines Used for Imbedded Systems * + * Fast Trigonometric Routines Used for Embedded Systems * * Programmer: Bob McGwier, IDA CCR-P, June, 2000 * ***************************************************************/ /* This file is part of a program that implements a Software-Defined Radio. diff --git a/jDttSP/keyd.c b/jDttSP/keyd.c index 091f665..12e7797 100644 --- a/jDttSP/keyd.c +++ b/jDttSP/keyd.c @@ -47,8 +47,6 @@ Bridgewater, NJ 08807 #define SAMP_RATE (48000) // # times key is sampled per sec -// > 64 requires root on Linux -//#define RTC_RATE (128) #define RTC_RATE (64) // # samples generated during 1 clock tick at RTC_RATE diff --git a/jDttSP/local.h b/jDttSP/local.h index 942fc2b..dda789e 100644 --- a/jDttSP/local.h +++ b/jDttSP/local.h @@ -39,11 +39,11 @@ Bridgewater, NJ 08807 /* #include */ /* #include */ -#include - #ifndef _local_h #define _local_h +#include + #define RCBASE ".DttSPrc" #define PARMPATH "./IPC/SDR-1000-0-commands.fifo" #define METERPATH "./IPC/SDR-1000-0-meter.chan" @@ -68,7 +68,7 @@ extern struct _loc { } path; struct { REAL rate; - int size; + int size, nrx; SDRMODE mode; } def; struct { int ring, meter; } mult; diff --git a/jDttSP/main.c b/jDttSP/main.c index 4aa5884..b68ba95 100644 --- a/jDttSP/main.c +++ b/jDttSP/main.c @@ -171,7 +171,15 @@ run_swch(void) { top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; } uni.mode.trx = top.swch.trx.next; - rx.tick = tx.tick = 0; + + // move this out of main! ----------------------------- + { + int k; + for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0; + } + tx.tick = 0; + //----------------------------------------------------- + top.state = top.swch.run.last; top.swch.bfct.want = top.swch.bfct.have = 0; @@ -440,6 +448,7 @@ setup_defaults(void) { loc.def.rate = DEFRATE; loc.def.size = DEFSIZE; loc.def.mode = DEFMODE; + loc.def.nrx = MAXRX; loc.mult.ring = RINGMULT; loc.mult.meter = METERMULT; diff --git a/jDttSP/sdr.c b/jDttSP/sdr.c index 5028ac3..d500676 100644 --- a/jDttSP/sdr.c +++ b/jDttSP/sdr.c @@ -73,133 +73,141 @@ setup_all(void) { } } + uni.multirx.lis = 0; + uni.multirx.nrx = loc.def.nrx; uni.tick = 0; } /* purely rx */ PRIVATE void -setup_rx(void) { +setup_rx(int k) { /* conditioning */ - rx.iqfix = newCorrectIQ(0.0, 1.0); - rx.filt.coef = newFIR_Bandpass_COMPLEX(-4800.0, - 4800.0, - uni.samplerate, - uni.buflen + 1); - rx.filt.ovsv = newFiltOvSv(FIRcoef(rx.filt.coef), - FIRsize(rx.filt.coef), - uni.wisdom.bits); - normalize_vec_COMPLEX(rx.filt.ovsv->zfvec, - rx.filt.ovsv->fftlen); + rx[k].iqfix = newCorrectIQ(0.0, 1.0); + rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0, + 4800.0, + uni.samplerate, + uni.buflen + 1); + rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef), + FIRsize(rx[k].filt.coef), + uni.wisdom.bits); + normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec, + rx[k].filt.ovsv->fftlen); // hack for EQ - rx.filt.save = newvec_COMPLEX(rx.filt.ovsv->fftlen, "RX filter cache"); - memcpy((char *) rx.filt.save, - (char *) rx.filt.ovsv->zfvec, - rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + rx[k].filt.save = newvec_COMPLEX(rx[k].filt.ovsv->fftlen, "RX filter cache"); + memcpy((char *) rx[k].filt.save, + (char *) rx[k].filt.ovsv->zfvec, + rx[k].filt.ovsv->fftlen * sizeof(COMPLEX)); /* buffers */ /* note we overload the internal filter buffers we just created */ - rx.buf.i = newCXB(FiltOvSv_fetchsize(rx.filt.ovsv), - FiltOvSv_fetchpoint(rx.filt.ovsv), - "init rx.buf.i"); - rx.buf.o = newCXB(FiltOvSv_storesize(rx.filt.ovsv), - FiltOvSv_storepoint(rx.filt.ovsv), - "init rx.buf.o"); + rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv), + FiltOvSv_fetchpoint(rx[k].filt.ovsv), + "init rx.buf.i"); + rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv), + FiltOvSv_storepoint(rx[k].filt.ovsv), + "init rx[k].buf.o"); /* conversion */ - rx.osc.freq = -11025.0; - rx.osc.phase = 0.0; - rx.osc.gen = newOSC(uni.buflen, - ComplexTone, - rx.osc.freq, - rx.osc.phase, - uni.samplerate, - "SDR RX Oscillator"); - - rx.agc.gen = newDigitalAgc(agcMED, // Mode + rx[k].osc.freq = -11025.0; + rx[k].osc.phase = 0.0; + rx[k].osc.gen = newOSC(uni.buflen, + ComplexTone, + rx[k].osc.freq, + rx[k].osc.phase, + uni.samplerate, + "SDR RX Oscillator"); + + rx[k].agc.gen = newDigitalAgc(agcMED, // Mode 7, // Hang 7, // Size 48, // Ramp 3, // Over 3, // Rcov - CXBsize(rx.buf.o), // BufSize + CXBsize(rx[k].buf.o), // BufSize 100.0, // MaxGain 0.707, // Limit 1.0, // CurGain - CXBbase(rx.buf.o)); - rx.agc.flag = TRUE; + CXBbase(rx[k].buf.o)); + rx[k].agc.flag = TRUE; /* demods */ - rx.am.gen = newAMD(48000.0, // REAL samprate - 0.0, // REAL f_initial - -500.0, // REAL f_lobound, - 500.0, // REAL f_hibound, - 400.0, // REAL f_bandwid, - CXBsize(rx.buf.o), // int size, - CXBbase(rx.buf.o), // COMPLEX *ivec, - CXBbase(rx.buf.o), // COMPLEX *ovec, - AMdet, // AM Mode AMdet == rectifier, + rx[k].am.gen = newAMD(48000.0, // REAL samprate + 0.0, // REAL f_initial + -500.0, // REAL f_lobound, + 500.0, // REAL f_hibound, + 400.0, // REAL f_bandwid, + CXBsize(rx[k].buf.o), // int size, + CXBbase(rx[k].buf.o), // COMPLEX *ivec, + CXBbase(rx[k].buf.o), // COMPLEX *ovec, + AMdet, // AM Mode AMdet == rectifier, // SAMdet == synchronous detector - "AM detector blew"); // char *tag - rx.fm.gen = newFMD(48000, // REAL samprate - 0.0, // REAL f_initial - -6000.0, // REAL f_lobound - 6000.0, // REAL f_hibound - 10000.0, // REAL f_bandwid - CXBsize(rx.buf.o), // int size - CXBbase(rx.buf.o), // COMPLEX *ivec - CXBbase(rx.buf.o), // COMPLEX *ovec - "New FM Demod structure"); // char *error message; + "AM detector blew"); // char *tag + rx[k].fm.gen = newFMD(48000, // REAL samprate + 0.0, // REAL f_initial + -6000.0, // REAL f_lobound + 6000.0, // REAL f_hibound + 10000.0, // REAL f_bandwid + CXBsize(rx[k].buf.o), // int size + CXBbase(rx[k].buf.o), // COMPLEX *ivec + CXBbase(rx[k].buf.o), // COMPLEX *ovec + "New FM Demod structure"); // char *error message; /* noise reduction */ - rx.anf.gen = new_lmsr(rx.buf.o, // CXB signal, - 64, // int delay, - 0.01, // REAL adaptation_rate, - 0.00001, // REAL leakage, - 45, // int adaptive_filter_size, - LMADF_INTERFERENCE); - rx.anf.flag = FALSE; - rx.anr.gen = new_lmsr(rx.buf.o, // CXB signal, - 64, // int delay, - 0.01, // REAL adaptation_rate, - 0.00001, // REAL leakage, - 45, // int adaptive_filter_size, - LMADF_NOISE); - rx.anr.flag = FALSE; - - rx.nb.thresh = 3.3; - rx.nb.gen = new_noiseblanker(rx.buf.i, rx.nb.thresh); - rx.nb.flag = FALSE; - - rx.nb_sdrom.thresh = 2.5; - rx.nb_sdrom.gen = new_noiseblanker(rx.buf.i, rx.nb_sdrom.thresh); - rx.nb_sdrom.flag = FALSE; - - rx.spot.gen = newSpotToneGen(-12.0, // gain - 700.0, // freq - 5.0, // ms rise - 5.0, // ms fall - uni.buflen, - uni.samplerate); - - rx.scl.pre.val = 1.0; - rx.scl.pre.flag = FALSE; - rx.scl.post.val = 1.0; - rx.scl.post.flag = FALSE; - - memset((char *) &rx.squelch, 0, sizeof(rx.squelch)); - rx.squelch.thresh = -30.0; - rx.squelch.power = 0.0; - rx.squelch.flag = rx.squelch.running = rx.squelch.set = FALSE; - rx.squelch.num = (int) (0.0395 * uni.samplerate + 0.5); - - rx.mode = uni.mode.sdr; - rx.bin.flag = FALSE; - - rx.tick = 0; + rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_INTERFERENCE); + rx[k].anf.flag = FALSE; + rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_NOISE); + rx[k].anr.flag = FALSE; + + rx[k].nb.thresh = 3.3; + rx[k].nb.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb.thresh); + rx[k].nb.flag = FALSE; + + rx[k].nb_sdrom.thresh = 2.5; + rx[k].nb_sdrom.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb_sdrom.thresh); + rx[k].nb_sdrom.flag = FALSE; + + rx[k].spot.gen = newSpotToneGen(-12.0, // gain + 700.0, // freq + 5.0, // ms rise + 5.0, // ms fall + uni.buflen, + uni.samplerate); + + rx[k].scl.pre.val = 1.0; + rx[k].scl.pre.flag = FALSE; + rx[k].scl.post.val = 1.0; + rx[k].scl.post.flag = FALSE; + + memset((char *) &rx[k].squelch, 0, sizeof(rx[k].squelch)); + 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].mode = uni.mode.sdr; + rx[k].bin.flag = FALSE; + + { + REAL pos = 0.5, // 0 <= pos <= 1, left->right + theta = (1.0 - pos) * M_PI / 2.0; + rx[k].azim = Cmplx(cos(theta), sin(theta)); + } + + rx[k].tick = 0; } /* purely tx */ @@ -275,11 +283,23 @@ setup_tx(void) { void setup_workspace(void) { - setup_all(), setup_rx(), setup_tx(); + int k; + + setup_all(); + + for (k = 0; k < uni.multirx.nrx; k++) { + setup_rx(k); + uni.multirx.act[k] = FALSE; + } + uni.multirx.act[0] = TRUE; + uni.multirx.nac = 1; + + setup_tx(); } void destroy_workspace(void) { + int k; /* TX */ delSpeechProc(tx.spr.gen); @@ -293,22 +313,24 @@ destroy_workspace(void) { delCXB(tx.buf.i); /* RX */ - delSpotToneGen(rx.spot.gen); - delDigitalAgc(rx.agc.gen); - del_nb(rx.nb_sdrom.gen); - del_nb(rx.nb.gen); - del_lmsr(rx.anf.gen); - del_lmsr(rx.anr.gen); - delAMD(rx.am.gen); - delFMD(rx.fm.gen); - delOSC(rx.osc.gen); - delvec_COMPLEX(rx.filt.save); - delFiltOvSv(rx.filt.ovsv); - delFIR_Bandpass_COMPLEX(rx.filt.coef); - delCorrectIQ(rx.iqfix); - delCXB(rx.buf.o); - delCXB(rx.buf.i); - + for (k = 0; k < uni.multirx.nrx; k++) { + delSpotToneGen(rx[k].spot.gen); + delDigitalAgc(rx[k].agc.gen); + del_nb(rx[k].nb_sdrom.gen); + del_nb(rx[k].nb.gen); + del_lmsr(rx[k].anf.gen); + del_lmsr(rx[k].anr.gen); + delAMD(rx[k].am.gen); + delFMD(rx[k].fm.gen); + delOSC(rx[k].osc.gen); + delvec_COMPLEX(rx[k].filt.save); + delFiltOvSv(rx[k].filt.ovsv); + delFIR_Bandpass_COMPLEX(rx[k].filt.coef); + delCorrectIQ(rx[k].iqfix); + delCXB(rx[k].buf.o); + delCXB(rx[k].buf.i); + } + /* all */ if (uni.meter.flag) closeChan(uni.meter.chan.c); @@ -376,167 +398,189 @@ do_meter(COMPLEX *vec, int len) { /* RX processing */ PRIVATE BOOLEAN -should_do_rx_squelch(void) { - if (rx.squelch.flag) { - int i, n = CXBhave(rx.buf.o); - rx.squelch.power = 0.0; +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.squelch.power += Csqrmag(CXBdata(rx.buf.o, i)); - return rx.squelch.thresh > 10.0 * log10(rx.squelch.power); + rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i)); + return rx[k].squelch.thresh > 10.0 * log10(rx[k].squelch.power); } else - return rx.squelch.set = FALSE; + return rx[k].squelch.set = FALSE; } // apply squelch // slew into silence first time PRIVATE void -do_squelch(void) { - rx.squelch.set = TRUE; - if (!rx.squelch.running) { - int i, m = rx.squelch.num, n = CXBhave(rx.buf.o) - m; +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; for (i = 0; i < m; i++) - CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), 1.0 - (REAL) i / m); - memset((void *) (CXBbase(rx.buf.o) + m), 0, n * sizeof(COMPLEX)); - rx.squelch.running = TRUE; + 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.buf.o), 0, CXBhave(rx.buf.o) * sizeof(COMPLEX)); + memset((void *) CXBbase(rx[k].buf.o), 0, CXBhave(rx[k].buf.o) * sizeof(COMPLEX)); } // lift squelch // slew out from silence to full scale PRIVATE void -no_squelch(void) { - if (rx.squelch.running) { - int i, m = rx.squelch.num; +no_squelch(int k) { + if (rx[k].squelch.running) { + int i, m = rx[k].squelch.num; for (i = 0; i < m; i++) - CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), (REAL) i / m); - rx.squelch.running = FALSE; + CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m); + rx[k].squelch.running = FALSE; } } /* pre-condition for (nearly) all RX modes */ PRIVATE void -do_rx_pre(void) { - int i, n = min(CXBhave(rx.buf.i), uni.buflen); +do_rx_pre(int k) { + int i, n = min(CXBhave(rx[k].buf.i), uni.buflen); // - // do shrinkage here + // do shrinkage here? // - if (rx.scl.pre.flag) + if (rx[k].scl.pre.flag) for (i = 0; i < n; i++) - CXBdata(rx.buf.i, i) = Cscl(CXBdata(rx.buf.i, i), - rx.scl.pre.val); + CXBdata(rx[k].buf.i, i) = Cscl(CXBdata(rx[k].buf.i, i), + rx[k].scl.pre.val); - if (rx.nb.flag) noiseblanker(rx.nb.gen); - if (rx.nb_sdrom.flag) SDROMnoiseblanker(rx.nb_sdrom.gen); + if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen); + if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen); - correctIQ(rx.buf.i, rx.iqfix); + correctIQ(rx[k].buf.i, rx[k].iqfix); - /* 2nd if conversion happens here */ - if (rx.osc.gen->Frequency != 0.0) { - ComplexOSC(rx.osc.gen); + /* 2nd IF conversion happens here */ + + if (rx[k].osc.gen->Frequency != 0.0) { + ComplexOSC(rx[k].osc.gen); for (i = 0; i < n; i++) - CXBdata(rx.buf.i, i) = Cmul(CXBdata(rx.buf.i, i), - OSCCdata(rx.osc.gen, i)); + CXBdata(rx[k].buf.i, i) = Cmul(CXBdata(rx[k].buf.i, i), + OSCCdata(rx[k].osc.gen, i)); } /* filtering, metering, squelch, & AGC */ - if (rx.mode != SPEC) { - if (rx.tick == 0) reset_OvSv(rx.filt.ovsv); - filter_OvSv(rx.filt.ovsv); - CXBhave(rx.buf.o) = CXBhave(rx.buf.i); - if (uni.meter.flag) do_meter(CXBbase(rx.buf.o), uni.buflen); - if (should_do_rx_squelch()) do_squelch(); - if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick); + + if (rx[k].mode != SPEC) { + + if (rx[k].tick == 0) + reset_OvSv(rx[k].filt.ovsv); + + filter_OvSv(rx[k].filt.ovsv); + CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i); + + if (uni.meter.flag) + do_meter(CXBbase(rx[k].buf.o), uni.buflen); + + if (should_do_rx_squelch(k)) + do_squelch(k); + + else if (rx[k].agc.flag) + DigitalAgc(rx[k].agc.gen, rx[k].tick); + } else if (uni.meter.flag) - do_meter(CXBbase(rx.buf.o), uni.buflen); + do_meter(CXBbase(rx[k].buf.o), uni.buflen); } PRIVATE void -do_rx_post(void) { - int i, n = CXBhave(rx.buf.o); +do_rx_post(int k) { + int i, n = CXBhave(rx[k].buf.o); - if (!rx.squelch.set) { - no_squelch(); + if (!rx[k].squelch.set) { + no_squelch(k); // spotting tone - if (rx.spot.flag) { + if (rx[k].spot.flag) { // remember whether it's turned itself off during this pass - rx.spot.flag = SpotTone(rx.spot.gen); + rx[k].spot.flag = SpotTone(rx[k].spot.gen); for (i = 0; i < n; i++) - CXBdata(rx.buf.o, i) = Cadd(CXBdata(rx.buf.o, i), - CXBdata(rx.spot.gen->buf, i)); + CXBdata(rx[k].buf.o, i) = Cadd(CXBdata(rx[k].buf.o, i), + CXBdata(rx[k].spot.gen->buf, i)); } } // - // mix in sidetone here + // mix in sidetone here? // - if (rx.scl.post.flag) + // final scaling + + if (rx[k].scl.post.flag) for (i = 0; i < n; i++) - CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), - rx.scl.post.val); + CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), + rx[k].scl.post.val); - // not binaural? collapse - if (!rx.bin.flag) + // not binaural? + // position in stereo field + + if (!rx[k].bin.flag) + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i)); + +#if 0 + if (!rx[k].bin.flag) for (i = 0; i < n; i++) - CXBimag(rx.buf.o, i) = CXBreal(rx.buf.o, i); + CXBimag(rx[k].buf.o, i) = CXBreal(rx[k].buf.o, i); +#endif } /* demod processing */ PRIVATE void -do_rx_SBCW(void) { - if (rx.anr.flag) lmsr_adapt(rx.anr.gen); - if (rx.anf.flag) lmsr_adapt(rx.anf.gen); +do_rx_SBCW(int k) { + if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen); + if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen); } PRIVATE void -do_rx_AM(void) { AMDemod(rx.am.gen); } +do_rx_AM(int k) { AMDemod(rx[k].am.gen); } PRIVATE void -do_rx_FM(void) { FMDemod(rx.fm.gen); } +do_rx_FM(int k) { FMDemod(rx[k].fm.gen); } PRIVATE void -do_rx_DRM(void) {} +do_rx_DRM(int k) {} PRIVATE void -do_rx_SPEC(void) { - memcpy(CXBbase(rx.buf.o), - CXBbase(rx.buf.i), - sizeof(COMPLEX) * CXBhave(rx.buf.i)); - if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick); +do_rx_SPEC(int k) { + memcpy(CXBbase(rx[k].buf.o), + CXBbase(rx[k].buf.i), + sizeof(COMPLEX) * CXBhave(rx[k].buf.i)); + if (rx[k].agc.flag) DigitalAgc(rx[k].agc.gen, rx[k].tick); } PRIVATE void -do_rx_NIL(void) { - int i, n = min(CXBhave(rx.buf.i), uni.buflen); - for (i = 0; i < n; i++) CXBdata(rx.buf.o, i) = cxzero; +do_rx_NIL(int k) { + int i, n = min(CXBhave(rx[k].buf.i), uni.buflen); + for (i = 0; i < n; i++) CXBdata(rx[k].buf.o, i) = cxzero; } /* overall dispatch for RX processing */ PRIVATE void -do_rx(void) { - do_rx_pre(); - switch (rx.mode) { +do_rx(int k) { + do_rx_pre(k); + switch (rx[k].mode) { case USB: case LSB: case CWU: case CWL: - case DSB: do_rx_SBCW(); break; + case DSB: do_rx_SBCW(k); break; case AM: - case SAM: do_rx_AM(); break; - case FMN: do_rx_FM(); break; - case DRM: do_rx_DRM(); break; + case SAM: do_rx_AM(k); break; + case FMN: do_rx_FM(k); break; + case DRM: do_rx_DRM(k); break; case SPEC: - default: do_rx_SPEC(); break; + default: do_rx_SPEC(k); break; } - do_rx_post(); + do_rx_post(k); } //============================================================== @@ -660,29 +704,42 @@ do_tx(void) { void process_samples(float *bufl, float *bufr, int n) { - int i; + int i, k; switch (uni.mode.trx) { case RX: - for (i = 0; i < n; i++) - CXBimag(rx.buf.i, i) = bufl[i], CXBreal(rx.buf.i, i) = bufr[i]; - CXBhave(rx.buf.i) = n; - do_rx(), rx.tick++; + // make copies of the input for all receivers + for (k = 0; k < uni.multirx.nrx; k++) + if (uni.multirx.act[k]) { + for (i = 0; i < n; i++) + CXBimag(rx[k].buf.i, i) = bufl[i], CXBreal(rx[k].buf.i, i) = bufr[i]; + CXBhave(rx[k].buf.i) = n; + } + + // prepare buffers for mixing + memset((char *) bufl, 0, n * sizeof(float)); + memset((char *) bufr, 0, n * sizeof(float)); + + // run all receivers + for (k = 0; k < uni.multirx.nrx; k++) + if (uni.multirx.act[k]) { + 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); + CXBhave(rx[k].buf.o) = n; + } - for (i = 0; i < n; i++) - bufl[i] = (float) CXBimag(rx.buf.o, i), bufr[i] = (float) CXBreal(rx.buf.o, i); - CXBhave(rx.buf.o) = n; break; case TX: for (i = 0; i < n; i++) CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i]; CXBhave(tx.buf.i) = n; - do_tx(), tx.tick++; - for (i = 0; i < n; i++) bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i); CXBhave(tx.buf.o) = n; diff --git a/jDttSP/sdrexport.c b/jDttSP/sdrexport.c index 1830bd5..4213ef3 100644 --- a/jDttSP/sdrexport.c +++ b/jDttSP/sdrexport.c @@ -34,6 +34,6 @@ Bridgewater, NJ 08807 #include struct _uni uni; -struct _rx rx; +struct _rx rx[MAXRX]; struct _tx tx; struct _top top; diff --git a/jDttSP/sdrexport.h b/jDttSP/sdrexport.h index 860e3bf..4802d5b 100644 --- a/jDttSP/sdrexport.h +++ b/jDttSP/sdrexport.h @@ -36,6 +36,9 @@ Bridgewater, NJ 08807 #include +//------------------------------------------------------------------------ +// max no. simultaneous receivers +#define MAXRX (4) //------------------------------------------------------------------------ /* modulation types, modes */ typedef enum _sdrmode { @@ -93,6 +96,11 @@ extern struct _uni { int bits; } wisdom; + struct { + BOOLEAN act[MAXRX]; + int lis, nac, nrx; + } multirx; + long tick; } uni; @@ -152,9 +160,10 @@ extern struct _rx { } squelch; SDRMODE mode; struct { BOOLEAN flag; } bin; - long tick; REAL norm; -} rx; + COMPLEX azim; + long tick; +} rx[MAXRX]; //------------------------------------------------------------------------ /* TX */ diff --git a/jDttSP/update.c b/jDttSP/update.c index a090b76..aeed07b 100644 --- a/jDttSP/update.c +++ b/jDttSP/update.c @@ -35,6 +35,11 @@ Bridgewater, NJ 08807 #include +//////////////////////////////////////////////////////////////////////////// +// for commands affecting RX, which RX is Listening + +#define RL (uni.multirx.lis) + //////////////////////////////////////////////////////////////////////////// PRIVATE REAL @@ -52,28 +57,32 @@ setRXFilter(int n, char **p) { if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1; if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2; if ((low_frequency + 10) >= high_frequency) return -3; - delFIR_COMPLEX(rx.filt.coef); + delFIR_COMPLEX(rx[RL].filt.coef); - rx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, - high_frequency, - uni.samplerate, - ncoef); + rx[RL].filt.coef = newFIR_Bandpass_COMPLEX(low_frequency, + high_frequency, + uni.samplerate, + ncoef); zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits); #ifdef LHS - for (i = 0; i < ncoef; i++) zcvec[i] = rx.filt.coef->coef[i]; + for (i = 0; i < ncoef; i++) + zcvec[i] = rx[RL].filt.coef->coef[i]; #else - for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = rx.filt.coef->coef[i]; + for (i = 0; i < ncoef; i++) + zcvec[fftlen - ncoef + i] = rx[RL].filt.coef->coef[i]; #endif - fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) rx.filt.ovsv->zfvec); + fftw_one(ptmp, + (fftw_complex *) zcvec, + (fftw_complex *) rx[RL].filt.ovsv->zfvec); fftw_destroy_plan(ptmp); delvec_COMPLEX(zcvec); - normalize_vec_COMPLEX(rx.filt.ovsv->zfvec, - rx.filt.ovsv->fftlen); - memcpy((char *) rx.filt.save, - (char *) rx.filt.ovsv->zfvec, - rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + normalize_vec_COMPLEX(rx[RL].filt.ovsv->zfvec, + rx[RL].filt.ovsv->fftlen); + memcpy((char *) rx[RL].filt.save, + (char *) rx[RL].filt.ovsv->zfvec, + rx[RL].filt.ovsv->fftlen * sizeof(COMPLEX)); return 0; } @@ -100,11 +109,15 @@ setTXFilter(int n, char **p) { zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter"); ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits); #ifdef LHS - for (i = 0; i < ncoef; i++) zcvec[i] = tx.filt.coef->coef[i]; + for (i = 0; i < ncoef; i++) + zcvec[i] = tx.filt.coef->coef[i]; #else - for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i]; + for (i = 0; i < ncoef; i++) + zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i]; #endif - fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) tx.filt.ovsv->zfvec); + fftw_one(ptmp, + (fftw_complex *) zcvec, + (fftw_complex *) tx.filt.ovsv->zfvec); fftw_destroy_plan(ptmp); delvec_COMPLEX(zcvec); normalize_vec_COMPLEX(tx.filt.ovsv->zfvec, @@ -137,12 +150,12 @@ setMode(int n, char **p) { switch (trx) { case TX: tx.mode = mode; break; case RX: - default: rx.mode = mode; break; + default: rx[RL].mode = mode; break; } } else - tx.mode = rx.mode = uni.mode.sdr = mode; - if (rx.mode == AM) rx.am.gen->mode = AMdet; - if (rx.mode == SAM) rx.am.gen->mode = SAMdet; + tx.mode = rx[RL].mode = uni.mode.sdr = mode; + if (rx[RL].mode == AM) rx[RL].am.gen->mode = AMdet; + if (rx[RL].mode == SAM) rx[RL].am.gen->mode = SAMdet; return 0; } @@ -157,10 +170,10 @@ setOsc(int n, char **p) { switch (trx) { case TX: tx.osc.gen->Frequency = newfreq; break; case RX: - default: rx.osc.gen->Frequency = newfreq; break; + default: rx[RL].osc.gen->Frequency = newfreq; break; } } else - tx.osc.gen->Frequency = rx.osc.gen->Frequency = newfreq; + tx.osc.gen->Frequency = rx[RL].osc.gen->Frequency = newfreq; return 0; } @@ -173,45 +186,45 @@ setSampleRate(int n, char **p) { PRIVATE int setNR(int n, char **p) { - rx.anr.flag = atoi(p[0]); + rx[RL].anr.flag = atoi(p[0]); return 0; } PRIVATE int setANF(int n, char **p) { - rx.anf.flag = atoi(p[0]); + rx[RL].anf.flag = atoi(p[0]); return 0; } PRIVATE int setNB(int n, char **p) { - rx.nb.flag = atoi(p[0]); + rx[RL].nb.flag = atoi(p[0]); return 0; } PRIVATE int setNBvals(int n, char **p) { - REAL threshold = atof(p[0]); - rx.nb.gen->threshold = rx.nb.thresh = threshold; + REAL threshold = atof(p[0]); + rx[RL].nb.gen->threshold = rx[RL].nb.thresh = threshold; return 0; } PRIVATE int setSDROM(int n, char **p) { - rx.nb_sdrom.flag = atoi(p[0]); + rx[RL].nb_sdrom.flag = atoi(p[0]); return 0; } PRIVATE int setSDROMvals(int n, char **p) { REAL threshold = atof(p[0]); - rx.nb_sdrom.gen->threshold = rx.nb_sdrom.thresh = threshold; + rx[RL].nb_sdrom.gen->threshold = rx[RL].nb_sdrom.thresh = threshold; return 0; } PRIVATE int setBIN(int n, char **p) { - rx.bin.flag = atoi(p[0]); + rx[RL].bin.flag = atoi(p[0]); return 0; } @@ -224,10 +237,10 @@ setfixedAGC(int n, char **p) { switch(trx) { case TX: tx.agc.gen->gain.now = gain; break; case RX: - default: rx.agc.gen->gain.now = gain; break; + default: rx[RL].agc.gen->gain.now = gain; break; } } else - tx.agc.gen->gain.now = rx.agc.gen->gain.now = gain; + tx.agc.gen->gain.now = rx[RL].agc.gen->gain.now = gain; return 0; } @@ -236,28 +249,28 @@ setRXAGC(int n, char **p) { int setit = atoi(p[0]); switch (setit) { case agcOFF: - rx.agc.gen->mode = agcOFF; - rx.agc.flag = TRUE; + rx[RL].agc.gen->mode = agcOFF; + rx[RL].agc.flag = TRUE; break; case agcSLOW: - rx.agc.gen->mode = agcSLOW; - rx.agc.gen->hang = 10; - rx.agc.flag = TRUE; + rx[RL].agc.gen->mode = agcSLOW; + rx[RL].agc.gen->hang = 10; + rx[RL].agc.flag = TRUE; break; case agcMED: - rx.agc.gen->mode = agcMED; - rx.agc.gen->hang = 6; - rx.agc.flag = TRUE; + rx[RL].agc.gen->mode = agcMED; + rx[RL].agc.gen->hang = 6; + rx[RL].agc.flag = TRUE; break; case agcFAST: - rx.agc.gen->mode = agcFAST; - rx.agc.gen->hang = 3; - rx.agc.flag = TRUE; + rx[RL].agc.gen->mode = agcFAST; + rx[RL].agc.gen->hang = 3; + rx[RL].agc.flag = TRUE; break; case agcLONG: - rx.agc.gen->mode = agcLONG; - rx.agc.gen->hang = 23; - rx.agc.flag = TRUE; + rx[RL].agc.gen->mode = agcLONG; + rx[RL].agc.gen->hang = 23; + rx[RL].agc.flag = TRUE; break; } return 0; @@ -266,14 +279,14 @@ setRXAGC(int n, char **p) { PRIVATE int setRXAGCCompression(int n, char **p) { REAL rxcompression = atof(p[0]); - rx.agc.gen->gain.top = pow(10.0 , rxcompression * 0.05); + rx[RL].agc.gen->gain.top = pow(10.0 , rxcompression * 0.05); return 0; } PRIVATE int setRXAGCHang(int n, char **p) { int hang = atoi(p[0]); - rx.agc.gen->hang = + rx[RL].agc.gen->hang = max(1, min(23, hang * uni.samplerate / (1e3 * uni.buflen))); @@ -283,7 +296,7 @@ setRXAGCHang(int n, char **p) { PRIVATE int setRXAGCLimit(int n, char **p) { REAL limit = atof(p[0]); - rx.agc.gen->gain.lim = 0.001 * limit; + rx[RL].agc.gen->gain.lim = 0.001 * limit; return 0; } @@ -387,7 +400,7 @@ apply_txeq_band(REAL lof, REAL dB, REAL hif) { REAL g = dB2lin(dB); COMPLEX *src = tx.filt.save, *trg = tx.filt.ovsv->zfvec; - for (i = lox; i < hix; i++) { + for (i = lox; i < hix; i++) { trg[i] = Cscl(src[i], g); if (i) { int j = l - i; @@ -400,6 +413,7 @@ apply_txeq_band(REAL lof, REAL dB, REAL hif) { // 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600 // approximates W2IHY bandcenters. // no args, revert to no EQ. + PRIVATE int setTXEQ(int n, char **p) { if (n < 3) { @@ -429,10 +443,10 @@ apply_rxeq_band(REAL lof, REAL dB, REAL hif) { int i, lox = f2x(lof), hix = f2x(hif), - l = rx.filt.ovsv->fftlen; + l = rx[RL].filt.ovsv->fftlen; REAL g = dB2lin(dB); - COMPLEX *src = rx.filt.save, - *trg = rx.filt.ovsv->zfvec; + COMPLEX *src = rx[RL].filt.save, + *trg = rx[RL].filt.ovsv->zfvec; for (i = lox; i < hix; i++) { trg[i] = Cscl(src[i], g); if (i) { @@ -446,9 +460,9 @@ PRIVATE int setRXEQ(int n, char **p) { if (n < 3) { // revert to no EQ - memcpy((char *) rx.filt.ovsv->zfvec, - (char *) rx.filt.save, - rx.filt.ovsv->fftlen * sizeof(COMPLEX)); + memcpy((char *) rx[RL].filt.ovsv->zfvec, + (char *) rx[RL].filt.save, + rx[RL].filt.ovsv->fftlen * sizeof(COMPLEX)); return 0; } else { int i; @@ -472,10 +486,10 @@ setANFvals(int n, char **p) { delay = atoi(p[1]); REAL gain = atof(p[2]), leak = atof(p[3]); - rx.anf.gen->adaptive_filter_size = taps; - rx.anf.gen->delay = delay; - rx.anf.gen->adaptation_rate = gain; - rx.anf.gen->leakage = leak; + rx[RL].anf.gen->adaptive_filter_size = taps; + rx[RL].anf.gen->delay = delay; + rx[RL].anf.gen->adaptation_rate = gain; + rx[RL].anf.gen->leakage = leak; return 0; } @@ -485,10 +499,10 @@ setNRvals(int n, char **p) { delay = atoi(p[1]); REAL gain = atof(p[2]), leak = atof(p[3]); - rx.anr.gen->adaptive_filter_size = taps; - rx.anr.gen->delay = delay; - rx.anr.gen->adaptation_rate = gain; - rx.anr.gen->leakage = leak; + rx[RL].anr.gen->adaptive_filter_size = taps; + rx[RL].anr.gen->delay = delay; + rx[RL].anr.gen->adaptation_rate = gain; + rx[RL].anr.gen->leakage = leak; return 0; } @@ -496,34 +510,34 @@ PRIVATE int setcorrectIQ(int n, char **p) { int phaseadjustment = atoi(p[0]), gainadjustment = atoi(p[1]); - rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; - rx.iqfix->gain = 1.0+ 0.001 * (REAL) gainadjustment; + rx[RL].iqfix->phase = 0.001 * (REAL) phaseadjustment; + rx[RL].iqfix->gain = 1.0+ 0.001 * (REAL) gainadjustment; return 0; } PRIVATE int setcorrectIQphase(int n, char **p) { int phaseadjustment = atoi(p[0]); - rx.iqfix->phase = 0.001 * (REAL) phaseadjustment; + rx[RL].iqfix->phase = 0.001 * (REAL) phaseadjustment; return 0; } PRIVATE int setcorrectIQgain(int n, char **p) { int gainadjustment = atoi(p[0]); - rx.iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment; + rx[RL].iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment; return 0; } PRIVATE int setSquelch(int n, char **p) { - rx.squelch.thresh = -atof(p[0]); + rx[RL].squelch.thresh = -atof(p[0]); return 0; } PRIVATE int setSquelchSt(int n, char **p) { - rx.squelch.flag = atoi(p[0]); + rx[RL].squelch.flag = atoi(p[0]); return 0; } @@ -546,29 +560,29 @@ setSpotToneVals(int n, char **p) { freq = atof(p[1]), rise = atof(p[2]), fall = atof(p[3]); - setSpotToneGenVals(rx.spot.gen, gain, freq, rise, fall); + setSpotToneGenVals(rx[RL].spot.gen, gain, freq, rise, fall); return 0; } PRIVATE int setSpotTone(int n, char **p) { if (atoi(p[0])) { - SpotToneOn(rx.spot.gen); - rx.spot.flag = TRUE; + SpotToneOn(rx[RL].spot.gen); + rx[RL].spot.flag = TRUE; } else - SpotToneOff(rx.spot.gen); + SpotToneOff(rx[RL].spot.gen); return 0; } PRIVATE int setRXPreScl(int n, char **p) { - rx.scl.pre.flag = atoi(p[0]); + rx[RL].scl.pre.flag = atoi(p[0]); return 0; } PRIVATE int setRXPreSclVal(int n, char **p) { - rx.scl.pre.val = dB2lin(atof(p[0])); + rx[RL].scl.pre.val = dB2lin(atof(p[0])); return 0; } @@ -586,13 +600,13 @@ setTXPreSclVal(int n, char **p) { PRIVATE int setRXPostScl(int n, char **p) { - rx.scl.post.flag = atoi(p[0]); + rx[RL].scl.post.flag = atoi(p[0]); return 0; } PRIVATE int setRXPostSclVal(int n, char **p) { - rx.scl.post.val = dB2lin(atof(p[0])); + rx[RL].scl.post.val = dB2lin(atof(p[0])); return 0; } @@ -647,6 +661,88 @@ setRingBufferReset(int n, char **p) { return 0; } +PRIVATE int +setRXListen(int n, char **p) { + int lis = atoi(p[0]); + if (lis < 0 || lis >= uni.multirx.nrx) + return -1; + else { + uni.multirx.lis = lis; + return 0; + } +} + +PRIVATE int +setRXOn(int n, char **p) { + if (n < 1) { + if (uni.multirx.act[RL]) + return -1; + else { + uni.multirx.act[RL] = TRUE; + uni.multirx.nac++; + rx[RL].tick = 0; + return 0; + } + } else { + int k = atoi(p[0]); + if (k < 0 || k >= uni.multirx.nrx) + return -1; + else { + if (uni.multirx.act[k]) + return -1; + else { + uni.multirx.act[k] = TRUE; + uni.multirx.nac++; + rx[k].tick = 0; + return 0; + } + } + } +} + +PRIVATE int +setRXOff(int n, char **p) { + if (n < 1) { + if (!uni.multirx.act[RL]) + return -1; + else { + uni.multirx.act[RL] = FALSE; + --uni.multirx.nac; + return 0; + } + } else { + int k = atoi(p[0]); + if (k < 0 || k >= uni.multirx.nrx) + return -1; + else { + if (!uni.multirx.act[k]) + return -1; + else { + uni.multirx.act[k] = FALSE; + --uni.multirx.nac; + return 0; + } + } + } +} + +PRIVATE int +setRXPan(int n, char **p) { + REAL pos, theta; + if (n < 1) { + pos = 0.5; + theta = (1.0 - pos) * M_PI / 2.0; + rx[RL].azim = Cmplx(cos(theta), sin(theta)); + return 0; + } else { + if ((pos = atof(p[0])) < 0.0 || pos > 1.0) + return -1; + theta = (1.0 - pos) * M_PI / 2.0; + rx[RL].azim = Cmplx(cos(theta), sin(theta)); + return 0; + } +} + //======================================================================== #include @@ -700,6 +796,10 @@ CTE update_cmds[] = { {"setfixedAGC", setfixedAGC}, {"setMonDump", setMonDump}, {"setRingBufferReset", setRingBufferReset}, + {"setRXListen", setRXListen}, + {"setRXOn", setRXOn}, + {"setRXOff", setRXOff}, + {"setRXPan", setRXPan}, { 0, 0 } }; -- 2.45.2