3 This file is part of a program that implements a Software-Defined Radio.
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 The authors can be reached by email at
29 The DTTS Microwave Society
36 //========================================================================
37 /* initialization and termination */
41 if (uni.meter.flag) { // reset metering completely
43 for (i = 0; i < RXMETERPTS; i++)
44 for (k = 0; k < MAXRX; k++)
45 uni.meter.rx.val[k][i] = uni.meter.rx.avg[k][i] = -200.0;
46 for (i = 0; i < TXMETERPTS; i++)
47 uni.meter.tx.val[i] = uni.meter.tx.avg[i] = -200.0;
52 reset_spectrum(void) {
54 reinit_spectrum(&uni.spec);
58 reset_counters(void) {
60 for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
64 //========================================================================
66 /* global and general info,
67 not specifically attached to
68 tx, rx, or scheduling */
73 uni.samplerate = loc.def.rate;
74 uni.buflen = loc.def.size;
75 uni.mode.sdr = loc.def.mode;
78 uni.wisdom.path = loc.path.wisdom;
79 uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE;
81 FILE *f = fopen(uni.wisdom.path, "r");
85 char *line = alloca(WBUFLEN);
86 fgets(line, WBUFLEN, f);
87 if ((strlen(line) > WSTRLEN) &&
88 (fftw_import_wisdom_from_string(line) != FFTW_FAILURE))
89 uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_MEASURE | FFTW_USE_WISDOM;
97 uni.meter.rx.type = SIGNAL_STRENGTH;
98 uni.meter.tx.type = SIGNAL_STRENGTH;
103 uni.spec.buflen = uni.buflen;
104 uni.spec.scale = SPEC_PWR;
105 uni.spec.type = SPEC_POST_FILT;
106 uni.spec.size = loc.def.spec;
107 uni.spec.planbits = uni.wisdom.bits;
108 init_spectrum(&uni.spec);
110 // set which receiver is listening to commands
112 uni.multirx.nrx = loc.def.nrx;
114 // set mixing of input from aux ports
115 uni.mix.rx.flag = uni.mix.tx.flag = FALSE;
116 uni.mix.rx.gain = uni.mix.tx.gain = 1.0;
118 uni.cpdlen = loc.def.comp;
129 rx[k].iqfix = newCorrectIQ(0.0, 1.0);
130 rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
134 rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef),
135 FIRsize(rx[k].filt.coef),
137 normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec,
138 rx[k].filt.ovsv->fftlen);
141 rx[k].filt.save = newvec_COMPLEX(rx[k].filt.ovsv->fftlen, "RX filter cache");
142 memcpy((char *) rx[k].filt.save,
143 (char *) rx[k].filt.ovsv->zfvec,
144 rx[k].filt.ovsv->fftlen * sizeof(COMPLEX));
147 /* note we overload the internal filter buffers
149 rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv),
150 FiltOvSv_fetchpoint(rx[k].filt.ovsv),
152 rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv),
153 FiltOvSv_storepoint(rx[k].filt.ovsv),
157 rx[k].osc.freq = -11025.0;
158 rx[k].osc.phase = 0.0;
159 rx[k].osc.gen = newOSC(uni.buflen,
164 "SDR RX Oscillator");
166 rx[k].agc.gen = newDigitalAgc(agcSLOW, // Mode
171 CXBsize(rx[k].buf.o), // BufSize
175 CXBbase(rx[k].buf.o));
176 rx[k].agc.flag = TRUE;
179 rx[k].am.gen = newAMD(48000.0, // REAL samprate
180 0.0, // REAL f_initial
181 -500.0, // REAL f_lobound,
182 500.0, // REAL f_hibound,
183 400.0, // REAL f_bandwid,
184 CXBsize(rx[k].buf.o), // int size,
185 CXBbase(rx[k].buf.o), // COMPLEX *ivec,
186 CXBbase(rx[k].buf.o), // COMPLEX *ovec,
187 AMdet, // AM Mode AMdet == rectifier,
188 // SAMdet == synchronous detector
189 "AM detector blew"); // char *tag
190 rx[k].fm.gen = newFMD(48000, // REAL samprate
191 0.0, // REAL f_initial
192 -6000.0, // REAL f_lobound
193 6000.0, // REAL f_hibound
194 10000.0, // REAL f_bandwid
195 CXBsize(rx[k].buf.o), // int size
196 CXBbase(rx[k].buf.o), // COMPLEX *ivec
197 CXBbase(rx[k].buf.o), // COMPLEX *ovec
198 "New FM Demod structure"); // char *error message;
200 /* noise reduction */
201 rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal,
203 0.01, // REAL adaptation_rate,
204 0.00001, // REAL leakage,
205 45, // int adaptive_filter_size,
207 rx[k].anf.flag = FALSE;
208 rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal,
210 0.01, // REAL adaptation_rate,
211 0.00001, // REAL leakage,
212 45, // int adaptive_filter_size,
214 rx[k].anr.flag = FALSE;
216 rx[k].nb.thresh = 3.3;
217 rx[k].nb.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb.thresh);
218 rx[k].nb.flag = FALSE;
220 rx[k].nb_sdrom.thresh = 2.5;
221 rx[k].nb_sdrom.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb_sdrom.thresh);
222 rx[k].nb_sdrom.flag = FALSE;
224 rx[k].spot.gen = newSpotToneGen(-12.0, // gain
231 rx[k].scl.pre.val = 1.0;
232 rx[k].scl.pre.flag = FALSE;
233 rx[k].scl.post.val = 1.0;
234 rx[k].scl.post.flag = FALSE;
236 memset((char *) &rx[k].squelch, 0, sizeof(rx[k].squelch));
237 rx[k].squelch.thresh = -30.0;
238 rx[k].squelch.power = 0.0;
239 rx[k].squelch.flag = rx[k].squelch.running = rx[k].squelch.set = FALSE;
240 rx[k].squelch.num = uni.buflen - 10;
242 rx[k].cpd.gen = newWSCompander(uni.cpdlen,
245 rx[k].cpd.flag = FALSE;
247 rx[k].mode = uni.mode.sdr;
248 rx[k].bin.flag = FALSE;
251 REAL pos = 0.5, // 0 <= pos <= 1, left->right
252 theta = (1.0 - pos) * M_PI / 2.0;
253 rx[k].azim = Cmplx(cos(theta), sin(theta));
265 tx.iqfix = newCorrectIQ(0.0, 1.0);
266 tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
270 tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
271 FIRsize(tx.filt.coef),
273 normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
274 tx.filt.ovsv->fftlen);
277 tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache");
278 memcpy((char *) tx.filt.save,
279 (char *) tx.filt.ovsv->zfvec,
280 tx.filt.ovsv->fftlen * sizeof(COMPLEX));
283 tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
284 FiltOvSv_fetchpoint(tx.filt.ovsv),
286 tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
287 FiltOvSv_storepoint(tx.filt.ovsv),
291 tx.dcb.gen = newDCBlocker(DCB_MED, tx.buf.i);
296 tx.osc.gen = newOSC(uni.buflen,
301 "SDR TX Oscillator");
304 tx.agc.gen = newDigitalAgc(agcFAST, // Mode
309 CXBsize(tx.buf.o), // BufSize
316 tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.o), CXBsize(tx.buf.o));
319 tx.cpd.gen = newWSCompander(uni.cpdlen,
325 tx.scl.pre.val = 1.0;
326 tx.scl.pre.flag = FALSE;
327 tx.scl.post.val = 1.0;
328 tx.scl.post.flag = FALSE;
330 tx.mode = uni.mode.sdr;
333 /* not much else to do for TX */
336 /* how the outside world sees it */
339 setup_workspace(void) {
344 for (k = 0; k < uni.multirx.nrx; k++) {
346 uni.multirx.act[k] = FALSE;
348 uni.multirx.act[0] = TRUE;
355 destroy_workspace(void) {
359 delSpeechProc(tx.spr.gen);
360 delDigitalAgc(tx.agc.gen);
362 delDCBlocker(tx.dcb.gen);
363 delvec_COMPLEX(tx.filt.save);
364 delFiltOvSv(tx.filt.ovsv);
365 delFIR_Bandpass_COMPLEX(tx.filt.coef);
366 delCorrectIQ(tx.iqfix);
371 for (k = 0; k < uni.multirx.nrx; k++) {
372 delSpotToneGen(rx[k].spot.gen);
373 delDigitalAgc(rx[k].agc.gen);
374 del_nb(rx[k].nb_sdrom.gen);
375 del_nb(rx[k].nb.gen);
376 del_lmsr(rx[k].anf.gen);
377 del_lmsr(rx[k].anr.gen);
378 delAMD(rx[k].am.gen);
379 delFMD(rx[k].fm.gen);
380 delOSC(rx[k].osc.gen);
381 delvec_COMPLEX(rx[k].filt.save);
382 delFiltOvSv(rx[k].filt.ovsv);
383 delFIR_Bandpass_COMPLEX(rx[k].filt.coef);
384 delCorrectIQ(rx[k].iqfix);
390 finish_spectrum(&uni.spec);
393 //////////////////////////////////////////////////////////////////////////
395 //////////////////////////////////////////////////////////////////////////
397 //========================================================================
404 for (i = 0; i < CXBhave(buff); i++)
405 sum += Csqrmag(CXBdata(buff, i));
409 //========================================================================
412 // unfortunate duplication here, due to
416 do_rx_meter(int k, CXB buf, int tap) {
417 COMPLEX *vec = CXBbase(buf);
418 int i, len = CXBhave(buf);
420 uni.meter.rx.val[k][tap] = 0;
422 switch (uni.meter.rx.type) {
423 case SIGNAL_STRENGTH:
424 for (i = 0; i < len; i++)
425 uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
426 if (tap == 3) rx[k].norm = uni.meter.rx.val[k][tap] / len;
427 uni.meter.rx.avg[k][tap] =
428 uni.meter.rx.val[k][tap] =
429 10.0 * log10(uni.meter.rx.val[k][tap] + 1e-20);
431 case AVG_SIGNAL_STRENGTH:
432 for (i = 0; i < len; i++)
433 uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
434 uni.meter.rx.val[k][tap] =
435 uni.meter.rx.avg[k][tap] =
436 0.9 * uni.meter.rx.avg[k][tap] + log10(uni.meter.rx.val[k][tap] + 1e-20);
439 for(i = 0; i < len; i++)
440 uni.meter.rx.val[k][tap] = max(fabs(vec[i].re), uni.meter.rx.val[k][tap]);
441 uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10);
444 for(i = 0; i < len; i++)
445 uni.meter.rx.val[k][tap] = max(fabs(vec[i].im), uni.meter.rx.val[k][tap]);
446 uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10);
449 uni.meter.rx.val[k][tap] = 20.0 * log10(rx[k].agc.gen->gain.now + 1e-80);
457 do_tx_meter(CXB buf, int tap) {
458 COMPLEX *vec = CXBbase(buf);
459 int i, len = CXBhave(buf);
461 uni.meter.tx.val[tap] = 0;
463 switch (uni.meter.tx.type) {
464 case AVG_SIGNAL_STRENGTH:
465 for (i = 0; i < len; i++)
466 uni.meter.tx.val[tap] += Csqrmag(vec[i]);
467 uni.meter.tx.val[tap] =
468 uni.meter.tx.avg[tap] =
469 0.9 * uni.meter.tx.avg[tap] + log10(uni.meter.tx.val[tap] + 1e-20);
471 case SIGNAL_STRENGTH:
472 for (i = 0; i < len; i++)
473 uni.meter.tx.val[tap] += Csqrmag(vec[i]);
474 uni.meter.tx.avg[tap] =
475 uni.meter.tx.val[tap] =
476 10.0 * log10(uni.meter.tx.val[tap] + 1e-20);
480 REAL tmp = 20.0 * log10(tx.agc.gen->gain.now);
481 uni.meter.tx.val[tap] =
482 tmp < 0.0 ? tmp : min(20.0, 20.0 * log10(tx.agc.gen->gain.raw));
486 for(i = 0, uni.meter.tx.val[tap] = 1e-20; i < CXBhave(tx.buf.o); i++)
487 uni.meter.tx.val[tap] += Csqrmag(CXBdata(tx.buf.o, i));
488 uni.meter.tx.val[tap] /= 2048.0;
491 for(i = 0, uni.meter.tx.val[tap] = 1e-20; i < CXBhave(tx.buf.o); i++)
492 uni.meter.tx.val[tap] = max(uni.meter.tx.val[tap],
493 Csqrmag(CXBdata(tx.buf.o,i)));
501 do_rx_spectrum(int k, CXB buf, int type) {
502 if (uni.spec.flag && k == uni.spec.rxk && type == uni.spec.type) {
503 memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
504 (char *) CXBbase(buf),
505 CXBsize(buf) * sizeof(COMPLEX));
506 uni.spec.fill = (uni.spec.fill + CXBsize(buf)) % uni.spec.size;
511 do_tx_spectrum(CXB buf) {
512 memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
513 (char *) CXBbase(buf),
514 CXBsize(buf) * sizeof(COMPLEX));
515 uni.spec.fill = (uni.spec.fill + CXBsize(buf)) % uni.spec.size;
518 //========================================================================
522 should_do_rx_squelch(int k) {
523 if (rx[k].squelch.flag) {
524 int i, n = CXBhave(rx[k].buf.o);
525 rx[k].squelch.power = 0.0;
527 for (i = 0; i < n; i++)
528 rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i));
531 10.0 * log10(rx[k].squelch.power) < rx[k].squelch.thresh;
534 return rx[k].squelch.set = FALSE;
538 // slew into silence first time
542 rx[k].squelch.set = TRUE;
544 if (!rx[k].squelch.running) {
546 m = rx[k].squelch.num,
547 n = CXBhave(rx[k].buf.o) - m;
549 for (i = 0; i < m; i++)
550 CXBdata(rx[k].buf.o, i) =
551 Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m);
553 memset((void *) (CXBbase(rx[k].buf.o) + m),
555 n * sizeof(COMPLEX));
556 rx[k].squelch.running = TRUE;
559 memset((void *) CXBbase(rx[k].buf.o),
561 CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
565 // slew out from silence to full scale
569 if (rx[k].squelch.running) {
570 int i, m = rx[k].squelch.num;
572 for (i = 0; i < m; i++)
573 CXBdata(rx[k].buf.o, i) =
574 Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m);
576 rx[k].squelch.running = FALSE;
580 /* pre-condition for (nearly) all RX modes */
584 int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
586 if (rx[k].scl.pre.flag)
587 for (i = 0; i < n; i++)
588 CXBdata(rx[k].buf.i, i) = Cscl(CXBdata(rx[k].buf.i, i),
591 if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen);
592 if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen);
594 // metering for uncorrected values here
596 do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_CONV);
598 correctIQ(rx[k].buf.i, rx[k].iqfix);
600 /* 2nd IF conversion happens here */
602 if (rx[k].osc.gen->Frequency != 0.0) {
603 ComplexOSC(rx[k].osc.gen);
604 for (i = 0; i < n; i++)
605 CXBdata(rx[k].buf.i, i) = Cmul(CXBdata(rx[k].buf.i, i),
606 OSCCdata(rx[k].osc.gen, i));
609 /* filtering, metering, spectrum, squelch, & AGC */
611 if (rx[k].mode == SPEC)
613 do_rx_spectrum(k, rx[k].buf.i, SPEC_SEMI_RAW);
617 do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_FILT);
618 do_rx_spectrum(k, rx[k].buf.i, SPEC_PRE_FILT);
621 reset_OvSv(rx[k].filt.ovsv);
623 filter_OvSv(rx[k].filt.ovsv);
624 CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i);
626 do_rx_meter(k, rx[k].buf.o, RXMETER_POST_FILT);
627 do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_FILT);
630 WSCompand(rx[k].cpd.gen);
632 if (should_do_rx_squelch(k))
635 else if (rx[k].agc.flag)
636 DigitalAgc(rx[k].agc.gen, rx[k].tick);
638 do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_AGC);
644 int i, n = CXBhave(rx[k].buf.o);
646 if (!rx[k].squelch.set) {
649 if (rx[k].spot.flag) {
650 // remember whether it's turned itself off during this pass
651 rx[k].spot.flag = SpotTone(rx[k].spot.gen);
652 for (i = 0; i < n; i++)
653 CXBdata(rx[k].buf.o, i) = Cadd(CXBdata(rx[k].buf.o, i),
654 CXBdata(rx[k].spot.gen->buf, i));
660 if (rx[k].scl.post.flag)
661 for (i = 0; i < n; i++)
662 CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i),
666 // position in stereo field
669 for (i = 0; i < n; i++)
670 CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i));
673 /* demod processing */
677 if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen);
678 if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen);
682 do_rx_AM(int k) { AMDemod(rx[k].am.gen); }
685 do_rx_FM(int k) { FMDemod(rx[k].fm.gen); }
692 memcpy(CXBbase(rx[k].buf.o),
693 CXBbase(rx[k].buf.i),
694 sizeof(COMPLEX) * CXBhave(rx[k].buf.i));
695 if (rx[k].agc.flag) DigitalAgc(rx[k].agc.gen, rx[k].tick);
700 int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
701 for (i = 0; i < n; i++) CXBdata(rx[k].buf.o, i) = cxzero;
704 /* overall dispatch for RX processing */
709 switch (rx[k].mode) {
714 case DSB: do_rx_SBCW(k); break;
716 case SAM: do_rx_AM(k); break;
717 case FMN: do_rx_FM(k); break;
718 case DRM: do_rx_DRM(k); break;
720 default: do_rx_SPEC(k); break;
725 //==============================================================
728 /* pre-condition for (nearly) all TX modes */
733 if (tx.scl.pre.flag) {
734 int i, n = CXBhave(tx.buf.i);
735 for (i = 0; i < n; i++)
736 CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0);
739 correctIQ(tx.buf.i, tx.iqfix);
741 if (tx.dcb.flag) DCBlock(tx.dcb.gen);
743 if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
744 filter_OvSv(tx.filt.ovsv);
750 CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
752 if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
754 // meter modulated signal
756 do_tx_meter(tx.buf.o, TXMETER_POST_MOD);
758 if (tx.scl.post.flag) {
759 int i, n = CXBhave(tx.buf.o);
760 for (i = 0; i < n; i++)
761 CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
764 if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
765 if (tx.cpd.flag) WSCompand(tx.cpd.gen);
768 do_tx_spectrum(tx.buf.o);
770 if (tx.osc.gen->Frequency != 0.0) {
772 ComplexOSC(tx.osc.gen);
773 for (i = 0; i < CXBhave(tx.buf.o); i++)
774 CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i));
778 /* modulator processing */
782 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
784 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
785 for (i = 0; i < n; i++) {
786 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99),
787 Cscl(CXBdata(tx.buf.o, i), -0.01));
788 CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc);
794 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
796 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
797 for (i = 0; i < n; i++) {
798 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
799 Cscl(CXBdata(tx.buf.o, i), -0.001));
800 CXBreal(tx.buf.o, i) =
801 0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re);
802 CXBimag(tx.buf.o, i) = 0.0;
808 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
809 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
810 for (i = 0; i < n; i++) {
811 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
812 Cscl(CXBdata(tx.buf.o, i), 0.001));
813 tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq;
814 if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI;
815 if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI;
816 CXBdata(tx.buf.o, i) =
817 Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999);
823 int i, n = min(CXBhave(tx.buf.i), uni.buflen);
824 for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
827 /* general TX processing dispatch */
837 case DSB: do_tx_SBCW(); break;
839 case SAM: do_tx_AM(); break;
840 case FMN: do_tx_FM(); break;
843 default: do_tx_NIL(); break;
848 //========================================================================
849 /* overall buffer processing;
850 come here when there are buffers to work on */
853 process_samples(float *bufl, float *bufr,
854 float *auxl, float *auxr,
858 switch (uni.mode.trx) {
862 // make copies of the input for all receivers
863 for (k = 0; k < uni.multirx.nrx; k++)
864 if (uni.multirx.act[k]) {
865 for (i = 0; i < n; i++)
866 CXBimag(rx[k].buf.i, i) = bufl[i], CXBreal(rx[k].buf.i, i) = bufr[i];
867 CXBhave(rx[k].buf.i) = n;
870 // prepare buffers for mixing
871 memset((char *) bufl, 0, n * sizeof(float));
872 memset((char *) bufr, 0, n * sizeof(float));
875 for (k = 0; k < uni.multirx.nrx; k++)
876 if (uni.multirx.act[k]) {
877 do_rx(k), rx[k].tick++;
879 for (i = 0; i < n; i++)
880 bufl[i] += (float) CXBimag(rx[k].buf.o, i),
881 bufr[i] += (float) CXBreal(rx[k].buf.o, i);
882 CXBhave(rx[k].buf.o) = n;
885 // late mixing of aux buffers
887 for (i = 0; i < n; i++)
888 bufl[i] += (float) (auxl[i] * uni.mix.rx.gain),
889 bufr[i] += (float) (auxr[i] * uni.mix.rx.gain);
895 // early mixing of aux buffers
897 for (i = 0; i < n; i++)
898 bufl[i] += (float) (auxl[i] * uni.mix.tx.gain),
899 bufr[i] += (float) (auxr[i] * uni.mix.tx.gain);
901 for (i = 0; i < n; i++)
902 CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
903 CXBhave(tx.buf.i) = n;
907 for (i = 0; i < n; i++)
908 bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i);
909 CXBhave(tx.buf.o) = n;