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 */
39 /* global and general info,
40 not specifically attached to
41 tx, rx, or scheduling */
46 uni.samplerate = loc.def.rate;
47 uni.buflen = loc.def.size;
48 uni.mode.sdr = loc.def.mode;
52 uni.meter.chan.path = loc.path.meter;
53 uni.meter.chan.size = loc.mult.ring * sizeof(REAL);
54 uni.meter.val = -200.0;
55 uni.meter.chan.c = openChan(uni.meter.chan.path, uni.meter.chan.size);
58 uni.wisdom.path = loc.path.wisdom;
59 uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE;
61 FILE *f = fopen(uni.wisdom.path, "r");
65 char *line = alloca(WBUFLEN);
66 fgets(line, WBUFLEN, f);
67 if ((strlen(line) > WSTRLEN) &&
68 (fftw_import_wisdom_from_string(line) != FFTW_FAILURE))
69 uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_MEASURE | FFTW_USE_WISDOM;
77 uni.multirx.nrx = loc.def.nrx;
79 uni.mix.rx.flag = uni.mix.tx.flag = FALSE;
80 uni.mix.rx.gain = uni.mix.tx.gain = 1.0;
91 rx[k].iqfix = newCorrectIQ(0.0, 1.0);
92 rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
96 rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef),
97 FIRsize(rx[k].filt.coef),
99 normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec,
100 rx[k].filt.ovsv->fftlen);
103 rx[k].filt.save = newvec_COMPLEX(rx[k].filt.ovsv->fftlen, "RX filter cache");
104 memcpy((char *) rx[k].filt.save,
105 (char *) rx[k].filt.ovsv->zfvec,
106 rx[k].filt.ovsv->fftlen * sizeof(COMPLEX));
109 /* note we overload the internal filter buffers
111 rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv),
112 FiltOvSv_fetchpoint(rx[k].filt.ovsv),
114 rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv),
115 FiltOvSv_storepoint(rx[k].filt.ovsv),
119 rx[k].osc.freq = -11025.0;
120 rx[k].osc.phase = 0.0;
121 rx[k].osc.gen = newOSC(uni.buflen,
126 "SDR RX Oscillator");
128 rx[k].agc.gen = newDigitalAgc(agcMED, // Mode
134 CXBsize(rx[k].buf.o), // BufSize
138 CXBbase(rx[k].buf.o));
139 rx[k].agc.flag = TRUE;
142 rx[k].am.gen = newAMD(48000.0, // REAL samprate
143 0.0, // REAL f_initial
144 -500.0, // REAL f_lobound,
145 500.0, // REAL f_hibound,
146 400.0, // REAL f_bandwid,
147 CXBsize(rx[k].buf.o), // int size,
148 CXBbase(rx[k].buf.o), // COMPLEX *ivec,
149 CXBbase(rx[k].buf.o), // COMPLEX *ovec,
150 AMdet, // AM Mode AMdet == rectifier,
151 // SAMdet == synchronous detector
152 "AM detector blew"); // char *tag
153 rx[k].fm.gen = newFMD(48000, // REAL samprate
154 0.0, // REAL f_initial
155 -6000.0, // REAL f_lobound
156 6000.0, // REAL f_hibound
157 10000.0, // REAL f_bandwid
158 CXBsize(rx[k].buf.o), // int size
159 CXBbase(rx[k].buf.o), // COMPLEX *ivec
160 CXBbase(rx[k].buf.o), // COMPLEX *ovec
161 "New FM Demod structure"); // char *error message;
163 /* noise reduction */
164 rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal,
166 0.01, // REAL adaptation_rate,
167 0.00001, // REAL leakage,
168 45, // int adaptive_filter_size,
170 rx[k].anf.flag = FALSE;
171 rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal,
173 0.01, // REAL adaptation_rate,
174 0.00001, // REAL leakage,
175 45, // int adaptive_filter_size,
177 rx[k].anr.flag = FALSE;
179 rx[k].nb.thresh = 3.3;
180 rx[k].nb.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb.thresh);
181 rx[k].nb.flag = FALSE;
183 rx[k].nb_sdrom.thresh = 2.5;
184 rx[k].nb_sdrom.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb_sdrom.thresh);
185 rx[k].nb_sdrom.flag = FALSE;
187 rx[k].spot.gen = newSpotToneGen(-12.0, // gain
194 rx[k].scl.pre.val = 1.0;
195 rx[k].scl.pre.flag = FALSE;
196 rx[k].scl.post.val = 1.0;
197 rx[k].scl.post.flag = FALSE;
199 memset((char *) &rx[k].squelch, 0, sizeof(rx[k].squelch));
200 rx[k].squelch.thresh = -30.0;
201 rx[k].squelch.power = 0.0;
202 rx[k].squelch.flag = rx[k].squelch.running = rx[k].squelch.set = FALSE;
203 rx[k].squelch.num = (int) (0.0395 * uni.samplerate + 0.5);
205 rx[k].mode = uni.mode.sdr;
206 rx[k].bin.flag = FALSE;
209 REAL pos = 0.5, // 0 <= pos <= 1, left->right
210 theta = (1.0 - pos) * M_PI / 2.0;
211 rx[k].azim = Cmplx(cos(theta), sin(theta));
212 fprintf(stderr, "azim %f %f\n", rx[k].azim.re, rx[k].azim.im);
224 tx.iqfix = newCorrectIQ(0.0, 1.0);
225 tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
229 tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
230 FIRsize(tx.filt.coef),
232 normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
233 tx.filt.ovsv->fftlen);
236 tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache");
237 memcpy((char *) tx.filt.save,
238 (char *) tx.filt.ovsv->zfvec,
239 tx.filt.ovsv->fftlen * sizeof(COMPLEX));
242 tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
243 FiltOvSv_fetchpoint(tx.filt.ovsv),
245 tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
246 FiltOvSv_storepoint(tx.filt.ovsv),
252 tx.osc.gen = newOSC(uni.buflen,
257 "SDR TX Oscillator");
259 tx.agc.gen = newDigitalAgc(agcFAST, // Mode
265 CXBsize(tx.buf.o), // BufSize
272 tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i));
276 tx.scl.pre.val = 1.0;
277 tx.scl.pre.flag = FALSE;
278 tx.scl.post.val = 1.0;
279 tx.scl.post.flag = FALSE;
281 tx.mode = uni.mode.sdr;
284 /* not much else to do for TX */
287 /* how the outside world sees it */
290 setup_workspace(void) {
295 for (k = 0; k < uni.multirx.nrx; k++) {
297 uni.multirx.act[k] = FALSE;
299 uni.multirx.act[0] = TRUE;
306 destroy_workspace(void) {
310 delSpeechProc(tx.spr.gen);
311 delDigitalAgc(tx.agc.gen);
313 delvec_COMPLEX(tx.filt.save);
314 delFiltOvSv(tx.filt.ovsv);
315 delFIR_Bandpass_COMPLEX(tx.filt.coef);
316 delCorrectIQ(tx.iqfix);
321 for (k = 0; k < uni.multirx.nrx; k++) {
322 delSpotToneGen(rx[k].spot.gen);
323 delDigitalAgc(rx[k].agc.gen);
324 del_nb(rx[k].nb_sdrom.gen);
325 del_nb(rx[k].nb.gen);
326 del_lmsr(rx[k].anf.gen);
327 del_lmsr(rx[k].anr.gen);
328 delAMD(rx[k].am.gen);
329 delFMD(rx[k].fm.gen);
330 delOSC(rx[k].osc.gen);
331 delvec_COMPLEX(rx[k].filt.save);
332 delFiltOvSv(rx[k].filt.ovsv);
333 delFIR_Bandpass_COMPLEX(rx[k].filt.coef);
334 delCorrectIQ(rx[k].iqfix);
341 closeChan(uni.meter.chan.c);
344 //////////////////////////////////////////////////////////////////////////
346 //////////////////////////////////////////////////////////////////////////
348 //========================================================================
355 for (i = 0; i < CXBhave(buff); i++)
356 sum += Csqrmag(CXBdata(buff, i));
360 //========================================================================
363 /* tap off S-meter from some buf */
366 do_meter(COMPLEX *vec, int len) {
371 switch (uni.meter.type) {
372 case AVG_SIGNAL_STRENGTH:
373 for (i = 0; i < len; i++)
374 uni.meter.val += Csqrmag(vec[i]);
376 uni.meter.avgval = 0.9 * uni.meter.avgval + log10(uni.meter.val + 1e-20);
378 case SIGNAL_STRENGTH:
379 for (i = 0; i < len; i++)
380 uni.meter.val += Csqrmag(vec[i]);
381 uni.meter.avgval = uni.meter.val = 10.0 * log10(uni.meter.val + 1e-20);
384 for(i = 0; i < len; i++)
385 uni.meter.val = max(fabs(vec[i].re), uni.meter.val);
386 uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10);
389 for(i = 0; i < len; i++)
390 uni.meter.val = max(fabs(vec[i].im), uni.meter.val);
391 uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10);
397 putChan_nowait(uni.meter.chan.c,
398 (char *) &uni.meter.val,
399 sizeof(uni.meter.val));
402 //========================================================================
406 should_do_rx_squelch(int k) {
407 if (rx[k].squelch.flag) {
408 int i, n = CXBhave(rx[k].buf.o);
409 rx[k].squelch.power = 0.0;
410 for (i = 0; i < n; i++)
411 rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i));
412 return rx[k].squelch.thresh > 10.0 * log10(rx[k].squelch.power);
414 return rx[k].squelch.set = FALSE;
418 // slew into silence first time
422 rx[k].squelch.set = TRUE;
423 if (!rx[k].squelch.running) {
424 int i, m = rx[k].squelch.num, n = CXBhave(rx[k].buf.o) - m;
425 for (i = 0; i < m; i++)
426 CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m);
427 memset((void *) (CXBbase(rx[k].buf.o) + m), 0, n * sizeof(COMPLEX));
428 rx[k].squelch.running = TRUE;
430 memset((void *) CXBbase(rx[k].buf.o), 0, CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
434 // slew out from silence to full scale
438 if (rx[k].squelch.running) {
439 int i, m = rx[k].squelch.num;
440 for (i = 0; i < m; i++)
441 CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m);
442 rx[k].squelch.running = FALSE;
446 /* pre-condition for (nearly) all RX modes */
450 int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
453 // do shrinkage here?
456 if (rx[k].scl.pre.flag)
457 for (i = 0; i < n; i++)
458 CXBdata(rx[k].buf.i, i) = Cscl(CXBdata(rx[k].buf.i, i),
461 if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen);
462 if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen);
464 correctIQ(rx[k].buf.i, rx[k].iqfix);
466 /* 2nd IF conversion happens here */
468 if (rx[k].osc.gen->Frequency != 0.0) {
469 ComplexOSC(rx[k].osc.gen);
470 for (i = 0; i < n; i++)
471 CXBdata(rx[k].buf.i, i) = Cmul(CXBdata(rx[k].buf.i, i),
472 OSCCdata(rx[k].osc.gen, i));
475 /* filtering, metering, squelch, & AGC */
477 if (rx[k].mode != SPEC) {
480 reset_OvSv(rx[k].filt.ovsv);
482 filter_OvSv(rx[k].filt.ovsv);
483 CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i);
486 do_meter(CXBbase(rx[k].buf.o), uni.buflen);
488 if (should_do_rx_squelch(k))
491 else if (rx[k].agc.flag)
492 DigitalAgc(rx[k].agc.gen, rx[k].tick);
494 } else if (uni.meter.flag)
495 do_meter(CXBbase(rx[k].buf.o), uni.buflen);
500 int i, n = CXBhave(rx[k].buf.o);
502 if (!rx[k].squelch.set) {
505 if (rx[k].spot.flag) {
506 // remember whether it's turned itself off during this pass
507 rx[k].spot.flag = SpotTone(rx[k].spot.gen);
508 for (i = 0; i < n; i++)
509 CXBdata(rx[k].buf.o, i) = Cadd(CXBdata(rx[k].buf.o, i),
510 CXBdata(rx[k].spot.gen->buf, i));
515 // mix in sidetone here?
520 if (rx[k].scl.post.flag)
521 for (i = 0; i < n; i++)
522 CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i),
526 // position in stereo field
529 for (i = 0; i < n; i++)
530 CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i));
534 for (i = 0; i < n; i++)
535 CXBimag(rx[k].buf.o, i) = CXBreal(rx[k].buf.o, i);
539 /* demod processing */
543 if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen);
544 if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen);
548 do_rx_AM(int k) { AMDemod(rx[k].am.gen); }
551 do_rx_FM(int k) { FMDemod(rx[k].fm.gen); }
558 memcpy(CXBbase(rx[k].buf.o),
559 CXBbase(rx[k].buf.i),
560 sizeof(COMPLEX) * CXBhave(rx[k].buf.i));
561 if (rx[k].agc.flag) DigitalAgc(rx[k].agc.gen, rx[k].tick);
566 int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
567 for (i = 0; i < n; i++) CXBdata(rx[k].buf.o, i) = cxzero;
570 /* overall dispatch for RX processing */
575 switch (rx[k].mode) {
580 case DSB: do_rx_SBCW(k); break;
582 case SAM: do_rx_AM(k); break;
583 case FMN: do_rx_FM(k); break;
584 case DRM: do_rx_DRM(k); break;
586 default: do_rx_SPEC(k); break;
591 //==============================================================
594 /* pre-condition for (nearly) all TX modes */
599 if (tx.scl.pre.flag) {
600 int i, n = CXBhave(tx.buf.i);
601 for (i = 0; i < n; i++)
602 CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0);
606 // mix in CW tone here?
609 correctIQ(tx.buf.i, tx.iqfix);
611 if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
613 if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
614 filter_OvSv(tx.filt.ovsv);
619 CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
621 if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
622 if (tx.scl.post.flag) {
623 int i, n = CXBhave(tx.buf.o);
624 for (i = 0; i < n; i++)
625 CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
627 if (uni.meter.flag) do_meter(CXBbase(tx.buf.o), uni.buflen);
628 if (tx.osc.gen->Frequency != 0.0) {
630 ComplexOSC(tx.osc.gen);
631 for (i = 0; i < CXBhave(tx.buf.o); i++)
632 CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i));
636 /* modulator processing */
640 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
642 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
643 for (i = 0; i < n; i++) {
644 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99),
645 Cscl(CXBdata(tx.buf.o, i), -0.01));
646 CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc);
652 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
654 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
655 for (i = 0; i < n; i++) {
656 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
657 Cscl(CXBdata(tx.buf.o, i), -0.001));
658 CXBreal(tx.buf.o, i) =
659 0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re);
660 CXBimag(tx.buf.o, i) = 0.0;
666 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
667 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
668 for (i = 0; i < n; i++) {
669 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
670 Cscl(CXBdata(tx.buf.o, i), 0.001));
671 tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq;
672 if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI;
673 if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI;
674 CXBdata(tx.buf.o, i) =
675 Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999);
681 int i, n = min(CXBhave(tx.buf.i), uni.buflen);
682 for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
685 /* general TX processing dispatch */
695 case DSB: do_tx_SBCW(); break;
697 case SAM: do_tx_AM(); break;
698 case FMN: do_tx_FM(); break;
701 default: do_tx_NIL(); break;
706 //========================================================================
707 /* overall buffer processing;
708 come here when there are buffers to work on */
711 process_samples(float *bufl, float *bufr,
712 float *auxl, float *auxr,
716 switch (uni.mode.trx) {
720 // make copies of the input for all receivers
721 for (k = 0; k < uni.multirx.nrx; k++)
722 if (uni.multirx.act[k]) {
723 for (i = 0; i < n; i++)
724 CXBimag(rx[k].buf.i, i) = bufl[i], CXBreal(rx[k].buf.i, i) = bufr[i];
725 CXBhave(rx[k].buf.i) = n;
728 // prepare buffers for mixing
729 memset((char *) bufl, 0, n * sizeof(float));
730 memset((char *) bufr, 0, n * sizeof(float));
733 for (k = 0; k < uni.multirx.nrx; k++)
734 if (uni.multirx.act[k]) {
735 do_rx(k), rx[k].tick++;
737 for (i = 0; i < n; i++)
738 bufl[i] += CXBimag(rx[k].buf.o, i),
739 bufr[i] += CXBreal(rx[k].buf.o, i);
740 CXBhave(rx[k].buf.o) = n;
744 for (i = 0; i < n; i++)
745 bufl[i] += auxl[i] * uni.mix.rx.gain,
746 bufr[i] += auxr[i] * uni.mix.rx.gain;
753 for (i = 0; i < n; i++)
754 bufl[i] += auxl[i] * uni.mix.tx.gain,
755 bufr[i] += auxr[i] * uni.mix.tx.gain;
757 for (i = 0; i < n; i++)
758 CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
759 CXBhave(tx.buf.i) = n;
763 for (i = 0; i < n; i++)
764 bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i);
765 CXBhave(tx.buf.o) = n;