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;
85 rx.iqfix = newCorrectIQ(0.0, 1.0);
86 rx.filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
90 rx.filt.ovsv = newFiltOvSv(FIRcoef(rx.filt.coef),
91 FIRsize(rx.filt.coef),
93 normalize_vec_COMPLEX(rx.filt.ovsv->zfvec,
94 rx.filt.ovsv->fftlen);
97 rx.filt.save = newvec_COMPLEX(rx.filt.ovsv->fftlen, "RX filter cache");
98 memcpy((char *) rx.filt.save,
99 (char *) rx.filt.ovsv->zfvec,
100 rx.filt.ovsv->fftlen * sizeof(COMPLEX));
103 /* note we overload the internal filter buffers
105 rx.buf.i = newCXB(FiltOvSv_fetchsize(rx.filt.ovsv),
106 FiltOvSv_fetchpoint(rx.filt.ovsv),
108 rx.buf.o = newCXB(FiltOvSv_storesize(rx.filt.ovsv),
109 FiltOvSv_storepoint(rx.filt.ovsv),
113 rx.osc.freq = -11025.0;
115 rx.osc.gen = newOSC(uni.buflen,
120 "SDR RX Oscillator");
122 rx.agc.gen = newDigitalAgc(agcMED, // Mode
128 CXBsize(rx.buf.o), // BufSize
136 rx.am.gen = newAMD(48000.0, // REAL samprate
137 0.0, // REAL f_initial
138 -500.0, // REAL f_lobound,
139 500.0, // REAL f_hibound,
140 400.0, // REAL f_bandwid,
141 CXBsize(rx.buf.o), // int size,
142 CXBbase(rx.buf.o), // COMPLEX *ivec,
143 CXBbase(rx.buf.o), // COMPLEX *ovec,
144 AMdet, // AM Mode AMdet == rectifier,
145 // SAMdet == synchronous detector
146 "AM detector blew"); // char *tag
147 rx.fm.gen = newFMD(48000, // REAL samprate
148 0.0, // REAL f_initial
149 -6000.0, // REAL f_lobound
150 6000.0, // REAL f_hibound
151 10000.0, // REAL f_bandwid
152 CXBsize(rx.buf.o), // int size
153 CXBbase(rx.buf.o), // COMPLEX *ivec
154 CXBbase(rx.buf.o), // COMPLEX *ovec
155 "New FM Demod structure"); // char *error message;
157 /* noise reduction */
158 rx.anf.gen = new_lmsr(rx.buf.o, // CXB signal,
160 0.01, // REAL adaptation_rate,
161 0.00001, // REAL leakage,
162 45, // int adaptive_filter_size,
165 rx.anr.gen = new_lmsr(rx.buf.o, // CXB signal,
167 0.01, // REAL adaptation_rate,
168 0.00001, // REAL leakage,
169 45, // int adaptive_filter_size,
174 rx.nb.gen = new_noiseblanker(rx.buf.i, rx.nb.thresh);
177 rx.nb_sdrom.thresh = 2.5;
178 rx.nb_sdrom.gen = new_noiseblanker(rx.buf.i, rx.nb_sdrom.thresh);
179 rx.nb_sdrom.flag = FALSE;
181 rx.spot.gen = newSpotToneGen(-12.0, // gain
188 rx.scl.pre.val = 1.0;
189 rx.scl.pre.flag = FALSE;
190 rx.scl.post.val = 1.0;
191 rx.scl.post.flag = FALSE;
193 memset((char *) &rx.squelch, 0, sizeof(rx.squelch));
194 rx.squelch.thresh = -30.0;
195 rx.squelch.power = 0.0;
196 rx.squelch.flag = rx.squelch.running = rx.squelch.set = FALSE;
197 rx.squelch.num = (int) (0.0395 * uni.samplerate + 0.5);
199 rx.mode = uni.mode.sdr;
211 tx.iqfix = newCorrectIQ(0.0, 1.0);
212 tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
216 tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
217 FIRsize(tx.filt.coef),
219 normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
220 tx.filt.ovsv->fftlen);
223 tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache");
224 memcpy((char *) tx.filt.save,
225 (char *) tx.filt.ovsv->zfvec,
226 tx.filt.ovsv->fftlen * sizeof(COMPLEX));
229 tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
230 FiltOvSv_fetchpoint(tx.filt.ovsv),
232 tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
233 FiltOvSv_storepoint(tx.filt.ovsv),
239 tx.osc.gen = newOSC(uni.buflen,
244 "SDR TX Oscillator");
246 tx.agc.gen = newDigitalAgc(agcFAST, // Mode
252 CXBsize(tx.buf.o), // BufSize
259 tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i));
263 tx.scl.pre.val = 1.0;
264 tx.scl.pre.flag = FALSE;
265 tx.scl.post.val = 1.0;
266 tx.scl.post.flag = FALSE;
268 tx.mode = uni.mode.sdr;
271 /* not much else to do for TX */
274 /* how the outside world sees it */
277 setup_workspace(void) {
278 setup_all(), setup_rx(), setup_tx();
282 destroy_workspace(void) {
285 delSpeechProc(tx.spr.gen);
286 delDigitalAgc(tx.agc.gen);
288 delvec_COMPLEX(tx.filt.save);
289 delFiltOvSv(tx.filt.ovsv);
290 delFIR_Bandpass_COMPLEX(tx.filt.coef);
291 delCorrectIQ(tx.iqfix);
296 delSpotToneGen(rx.spot.gen);
297 delDigitalAgc(rx.agc.gen);
298 del_nb(rx.nb_sdrom.gen);
300 del_lmsr(rx.anf.gen);
301 del_lmsr(rx.anr.gen);
305 delvec_COMPLEX(rx.filt.save);
306 delFiltOvSv(rx.filt.ovsv);
307 delFIR_Bandpass_COMPLEX(rx.filt.coef);
308 delCorrectIQ(rx.iqfix);
314 closeChan(uni.meter.chan.c);
317 //////////////////////////////////////////////////////////////////////////
319 //////////////////////////////////////////////////////////////////////////
321 //========================================================================
328 for (i = 0; i < CXBhave(buff); i++)
329 sum += Csqrmag(CXBdata(buff, i));
333 //========================================================================
336 /* tap off S-meter from some buf */
339 do_meter(COMPLEX *vec, int len) {
344 switch (uni.meter.type) {
345 case AVG_SIGNAL_STRENGTH:
346 for (i = 0; i < len; i++)
347 uni.meter.val += Csqrmag(vec[i]);
349 uni.meter.avgval = 0.9 * uni.meter.avgval + log10(uni.meter.val + 1e-20);
351 case SIGNAL_STRENGTH:
352 for (i = 0; i < len; i++)
353 uni.meter.val += Csqrmag(vec[i]);
354 uni.meter.avgval = uni.meter.val = 10.0 * log10(uni.meter.val + 1e-20);
357 for(i = 0; i < len; i++)
358 uni.meter.val = max(fabs(vec[i].re), uni.meter.val);
359 uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10);
362 for(i = 0; i < len; i++)
363 uni.meter.val = max(fabs(vec[i].im), uni.meter.val);
364 uni.meter.val = 20.0 * log10(uni.meter.val + 1e-10);
370 putChan_nowait(uni.meter.chan.c,
371 (char *) &uni.meter.val,
372 sizeof(uni.meter.val));
375 //========================================================================
379 should_do_rx_squelch(void) {
380 if (rx.squelch.flag) {
381 int i, n = CXBhave(rx.buf.o);
382 rx.squelch.power = 0.0;
383 for (i = 0; i < n; i++)
384 rx.squelch.power += Csqrmag(CXBdata(rx.buf.o, i));
385 return rx.squelch.thresh > 10.0 * log10(rx.squelch.power);
387 return rx.squelch.set = FALSE;
391 // slew into silence first time
395 rx.squelch.set = TRUE;
396 if (!rx.squelch.running) {
397 int i, m = rx.squelch.num, n = CXBhave(rx.buf.o) - m;
398 for (i = 0; i < m; i++)
399 CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), 1.0 - (REAL) i / m);
400 memset((void *) (CXBbase(rx.buf.o) + m), 0, n * sizeof(COMPLEX));
401 rx.squelch.running = TRUE;
403 memset((void *) CXBbase(rx.buf.o), 0, CXBhave(rx.buf.o) * sizeof(COMPLEX));
407 // slew out from silence to full scale
411 if (rx.squelch.running) {
412 int i, m = rx.squelch.num;
413 for (i = 0; i < m; i++)
414 CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i), (REAL) i / m);
415 rx.squelch.running = FALSE;
419 /* pre-condition for (nearly) all RX modes */
423 int i, n = min(CXBhave(rx.buf.i), uni.buflen);
430 for (i = 0; i < n; i++)
431 CXBdata(rx.buf.i, i) = Cscl(CXBdata(rx.buf.i, i),
434 if (rx.nb.flag) noiseblanker(rx.nb.gen);
435 if (rx.nb_sdrom.flag) SDROMnoiseblanker(rx.nb_sdrom.gen);
437 correctIQ(rx.buf.i, rx.iqfix);
439 /* 2nd if conversion happens here */
440 if (rx.osc.gen->Frequency != 0.0) {
441 ComplexOSC(rx.osc.gen);
442 for (i = 0; i < n; i++)
443 CXBdata(rx.buf.i, i) = Cmul(CXBdata(rx.buf.i, i),
444 OSCCdata(rx.osc.gen, i));
447 /* filtering, metering, & AGC */
448 if (rx.mode != SPEC) {
449 if (rx.tick == 0) reset_OvSv(rx.filt.ovsv);
450 filter_OvSv(rx.filt.ovsv);
451 CXBhave(rx.buf.o) = CXBhave(rx.buf.i);
452 if (uni.meter.flag) do_meter(CXBbase(rx.buf.o), uni.buflen);
453 if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick);
454 } else if (uni.meter.flag)
455 do_meter(CXBbase(rx.buf.o), uni.buflen);
460 int i, n = CXBhave(rx.buf.o);
462 if (!rx.squelch.set) {
466 // remember whether it's turned itself off during this pass
467 rx.spot.flag = SpotTone(rx.spot.gen);
468 for (i = 0; i < n; i++)
469 CXBdata(rx.buf.o, i) = Cadd(CXBdata(rx.buf.o, i),
470 CXBdata(rx.spot.gen->buf, i));
475 // mix in sidetone here
478 if (rx.scl.post.flag)
479 for (i = 0; i < n; i++)
480 CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i),
483 // not binaural? collapse
485 for (i = 0; i < n; i++)
486 CXBimag(rx.buf.o, i) = CXBreal(rx.buf.o, i);
489 /* demod processing */
493 if (rx.anr.flag) lmsr_adapt(rx.anr.gen);
494 if (rx.anf.flag) lmsr_adapt(rx.anf.gen);
498 do_rx_AM(void) { AMDemod(rx.am.gen); }
501 do_rx_FM(void) { FMDemod(rx.fm.gen); }
508 memcpy(CXBbase(rx.buf.o),
510 sizeof(COMPLEX) * CXBhave(rx.buf.i));
511 if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick);
516 int i, n = min(CXBhave(rx.buf.i), uni.buflen);
517 for (i = 0; i < n; i++) CXBdata(rx.buf.o, i) = cxzero;
520 /* overall dispatch for RX processing */
530 case DSB: do_rx_SBCW(); break;
532 case SAM: do_rx_AM(); break;
533 case FMN: do_rx_FM(); break;
534 case DRM: do_rx_DRM(); break;
536 default: do_rx_SPEC(); break;
541 //==============================================================
544 /* pre-condition for (nearly) all TX modes */
549 if (tx.scl.pre.flag) {
550 int i, n = CXBhave(tx.buf.i);
551 for (i = 0; i < n; i++)
552 CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0);
556 // mix in CW tone here?
559 correctIQ(tx.buf.i, tx.iqfix);
561 if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
563 if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
564 filter_OvSv(tx.filt.ovsv);
569 CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
571 if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
572 if (tx.scl.post.flag) {
573 int i, n = CXBhave(tx.buf.o);
574 for (i = 0; i < n; i++)
575 CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
577 if (uni.meter.flag) do_meter(CXBbase(tx.buf.o), uni.buflen);
578 if (tx.osc.gen->Frequency != 0.0) {
580 ComplexOSC(tx.osc.gen);
581 for (i = 0; i < CXBhave(tx.buf.o); i++)
582 CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i));
586 /* modulator processing */
590 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
592 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
593 for (i = 0; i < n; i++) {
594 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99),
595 Cscl(CXBdata(tx.buf.o, i), -0.01));
596 CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc);
602 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
604 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
605 for (i = 0; i < n; i++) {
606 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
607 Cscl(CXBdata(tx.buf.o, i), -0.001));
608 CXBreal(tx.buf.o, i) =
609 0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re);
610 CXBimag(tx.buf.o, i) = 0.0;
616 int i, n = min(CXBhave(tx.buf.o), uni.buflen);
617 if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
618 for (i = 0; i < n; i++) {
619 tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
620 Cscl(CXBdata(tx.buf.o, i), 0.001));
621 tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq;
622 if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI;
623 if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI;
624 CXBdata(tx.buf.o, i) =
625 Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999);
631 int i, n = min(CXBhave(tx.buf.i), uni.buflen);
632 for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
635 /* general TX processing dispatch */
645 case DSB: do_tx_SBCW(); break;
647 case SAM: do_tx_AM(); break;
648 case FMN: do_tx_FM(); break;
651 default: do_tx_NIL(); break;
656 //========================================================================
657 /* overall buffer processing;
658 come here when there are buffers to work on */
661 process_samples(float *bufl, float *bufr, int n) {
664 switch (uni.mode.trx) {
667 for (i = 0; i < n; i++)
668 CXBimag(rx.buf.i, i) = bufl[i], CXBreal(rx.buf.i, i) = bufr[i];
669 CXBhave(rx.buf.i) = n;
673 for (i = 0; i < n; i++)
674 bufl[i] = (float)CXBimag(rx.buf.o, i), bufr[i] = (float)CXBreal(rx.buf.o, i);
675 CXBhave(rx.buf.o) = n;
679 for (i = 0; i < n; i++)
680 CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
681 CXBhave(tx.buf.i) = n;
685 for (i = 0; i < n; i++)
686 bufl[i] = (float)CXBimag(tx.buf.o, i), bufr[i] = (float)CXBreal(tx.buf.o, i);
687 CXBhave(tx.buf.o) = n;