}
}
+ 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 */
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);
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);
/* 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);
}
//==============================================================
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;
#include <common.h>
+////////////////////////////////////////////////////////////////////////////
+// for commands affecting RX, which RX is Listening
+
+#define RL (uni.multirx.lis)
+
////////////////////////////////////////////////////////////////////////////
PRIVATE REAL
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;
}
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,
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;
}
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;
}
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;
}
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;
}
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;
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)));
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;
}
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;
// 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) {
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) {
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;
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;
}
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;
}
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;
}
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;
}
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;
}
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 <thunk.h>
{"setfixedAGC", setfixedAGC},
{"setMonDump", setMonDump},
{"setRingBufferReset", setRingBufferReset},
+ {"setRXListen", setRXListen},
+ {"setRXOn", setRXOn},
+ {"setRXOff", setRXOff},
+ {"setRXPan", setRXPan},
{ 0, 0 }
};