]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/sdr.c
Upgraded windows alternatives
[dttsp.git] / jDttSP / sdr.c
1 /* sdr.c
2
3 This file is part of a program that implements a Software-Defined Radio.
4
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
6
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.
11
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.
16
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
20
21 The authors can be reached by email at
22
23 ab2kt@arrl.net
24 or
25 rwmcgwier@comcast.net
26
27 or by paper mail at
28
29 The DTTS Microwave Society
30 6 Kathleen Place
31 Bridgewater, NJ 08807
32 */
33
34 #include <common.h>
35
36 //========================================================================
37 /* initialization and termination */
38
39 void
40 reset_meters(void) {  
41   if (uni.meter.flag) { // reset metering completely
42     int i, k;
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;
48   }
49 }
50
51 void
52 reset_spectrum(void) {  
53   if (uni.spec.flag)
54     reinit_spectrum(&uni.spec);
55 }
56
57 void
58 reset_counters(void) {
59   int k;
60   for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
61   tx.tick = 0;
62 }
63
64 //========================================================================
65
66 /* global and general info,
67    not specifically attached to
68    tx, rx, or scheduling */
69
70 PRIVATE void
71 setup_all(void) {
72   
73   uni.samplerate = loc.def.rate;
74   uni.buflen = loc.def.size;
75   uni.mode.sdr = loc.def.mode;
76   uni.mode.trx = RX;
77   
78   uni.wisdom.path = loc.path.wisdom;
79   uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE;
80   {
81     FILE *f = fopen(uni.wisdom.path, "r");
82     if (f) {
83 #define WBUFLEN 2048
84 #define WSTRLEN 64      
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;
90 #undef WSTRLEN
91 #undef WBUFLEN      
92       fclose(f);
93     }
94   }
95   
96   if (uni.meter.flag) {
97     uni.meter.rx.type = SIGNAL_STRENGTH;
98     uni.meter.tx.type = SIGNAL_STRENGTH;
99     reset_meters();
100   }
101   
102   uni.spec.rxk = 0;
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);
109   
110   // set which receiver is listening to commands
111   uni.multirx.lis = 0;
112   uni.multirx.nrx = loc.def.nrx;
113   
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;
117   
118   uni.cpdlen = loc.def.comp;
119
120   uni.tick = 0;
121 }
122
123 /* purely rx */
124
125 PRIVATE void
126 setup_rx(int k) {
127   
128   /* conditioning */
129   rx[k].iqfix = newCorrectIQ(0.0, 1.0);
130   rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
131                                             4800.0,
132                                             uni.samplerate,
133                                             uni.buflen + 1);
134   rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef),
135                                 FIRsize(rx[k].filt.coef),
136                                 uni.wisdom.bits);
137   normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec,
138                         rx[k].filt.ovsv->fftlen);
139
140   // hack for EQ
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));
145
146   /* buffers */
147   /* note we overload the internal filter buffers
148      we just created */
149   rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv),
150                        FiltOvSv_fetchpoint(rx[k].filt.ovsv),
151                        "init rx[k].buf.i");
152   rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv),
153                        FiltOvSv_storepoint(rx[k].filt.ovsv),
154                        "init rx[k].buf.o");
155   
156   /* conversion */
157   rx[k].osc.freq = -11025.0;
158   rx[k].osc.phase = 0.0;
159   rx[k].osc.gen = newOSC(uni.buflen,
160                          ComplexTone,
161                          rx[k].osc.freq,
162                          rx[k].osc.phase,
163                          uni.samplerate,
164                          "SDR RX Oscillator");
165
166   rx[k].agc.gen = newDigitalAgc(agcSLOW,        // Mode
167                                 7,              // Hang
168                                 48,             // Ramp
169                                 3,              // Over
170                                 3,              // Rcov
171                                 CXBsize(rx[k].buf.o),   // BufSize
172                                 2500.0,         // MaxGain
173                                 0.707,          // Limit
174                                 1.0,            // CurGain
175                                 CXBbase(rx[k].buf.o));
176   rx[k].agc.flag = TRUE;
177
178   /* demods */
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;
199
200   /* noise reduction */
201   rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal,
202                            64,          // int delay,
203                            0.01,                // REAL adaptation_rate,
204                            0.00001,     // REAL leakage,
205                            45,          // int adaptive_filter_size,
206                            LMADF_INTERFERENCE);
207   rx[k].anf.flag = FALSE;
208   rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal,
209                            64,          // int delay,
210                            0.01,                // REAL adaptation_rate,
211                            0.00001,     // REAL leakage,
212                            45,          // int adaptive_filter_size,
213                            LMADF_NOISE);
214   rx[k].anr.flag = FALSE;
215
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;
219
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;
223
224   rx[k].spot.gen = newSpotToneGen(-12.0,        // gain
225                                   700.0,        // freq
226                                   5.0,  // ms rise
227                                   5.0,  // ms fall
228                                   uni.buflen,
229                                   uni.samplerate);
230
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;
235
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;
241
242   rx[k].cpd.gen = newWSCompander(uni.cpdlen,
243                                  0.0,
244                                  rx[k].buf.o);
245   rx[k].cpd.flag = FALSE;
246
247   rx[k].mode = uni.mode.sdr;
248   rx[k].bin.flag = FALSE;
249
250   {
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));
254   }
255
256   rx[k].tick = 0;
257 }
258
259 /* purely tx */
260
261 PRIVATE void
262 setup_tx(void) {
263
264   /* conditioning */
265   tx.iqfix = newCorrectIQ(0.0, 1.0);
266   tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
267                                          3000.0,
268                                          uni.samplerate,
269                                          uni.buflen + 1);
270   tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
271                              FIRsize(tx.filt.coef),
272                              uni.wisdom.bits);
273   normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
274                         tx.filt.ovsv->fftlen);
275
276   // hack for EQ
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));
281
282   /* buffers */
283   tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
284                     FiltOvSv_fetchpoint(tx.filt.ovsv),
285                     "init tx.buf.i");
286   tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
287                     FiltOvSv_storepoint(tx.filt.ovsv),
288                     "init tx.buf.o");
289   
290   tx.dcb.flag = FALSE;
291   tx.dcb.gen = newDCBlocker(DCB_MED, tx.buf.i);
292
293   /* conversion */
294   tx.osc.freq = 0.0;
295   tx.osc.phase = 0.0;
296   tx.osc.gen = newOSC(uni.buflen,
297                       ComplexTone,
298                       tx.osc.freq,
299                       tx.osc.phase,
300                       uni.samplerate,
301                       "SDR TX Oscillator");
302
303
304   tx.agc.gen = newDigitalAgc(agcFAST,   // Mode
305                              3,         // Hang
306                              48,        // Ramp
307                              3,         // Over
308                              3,         // Rcov
309                              CXBsize(tx.buf.o), // BufSize
310                              1.0,       // MaxGain
311                              0.900,     // Limit
312                              1.0,       // CurGain
313                              CXBbase(tx.buf.o));
314   tx.agc.flag = TRUE;
315
316   tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.o));
317   tx.spr.flag = FALSE;
318
319   tx.cpd.gen = newWSCompander(uni.cpdlen,
320                               0.0,
321                               tx.buf.i);
322   tx.cpd.flag = FALSE;
323
324   tx.scl.dc = cxzero;
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;
329
330   tx.mode = uni.mode.sdr;
331
332   tx.tick = 0;
333   /* not much else to do for TX */
334 }
335
336 /* how the outside world sees it */
337
338 void
339 setup_workspace(void) {
340   int k;
341
342   setup_all();
343
344   for (k = 0; k < uni.multirx.nrx; k++) {
345     setup_rx(k);
346     uni.multirx.act[k] = FALSE;
347   }
348   uni.multirx.act[0] = TRUE;
349   uni.multirx.nac = 1;
350   
351   setup_tx();
352 }
353
354 void
355 destroy_workspace(void) {
356   int k;
357
358   /* TX */
359   delSpeechProc(tx.spr.gen);
360   delDigitalAgc(tx.agc.gen);
361   delOSC(tx.osc.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);
367   delCXB(tx.buf.o);
368   delCXB(tx.buf.i);
369
370   /* RX */
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);
385     delCXB(rx[k].buf.o);
386     delCXB(rx[k].buf.i);
387   }
388   
389   /* all */
390   finish_spectrum(&uni.spec);
391 }
392
393 //////////////////////////////////////////////////////////////////////////
394 // execution
395 //////////////////////////////////////////////////////////////////////////
396
397 //========================================================================
398 // util
399
400 PRIVATE REAL
401 CXBnorm(CXB buff) {
402   int i;
403   double sum = 0.0;
404   for (i = 0; i < CXBhave(buff); i++)
405     sum += Csqrmag(CXBdata(buff, i));
406   return sqrt(sum);
407 }
408
409 //========================================================================
410 /* all */
411
412 // unfortunate duplication here, due to
413 // multirx vs monotx
414
415 PRIVATE void
416 do_rx_meter(int k, CXB buf, int tap) {
417   COMPLEX *vec = CXBbase(buf);
418   int i, len = CXBhave(buf);
419   
420   uni.meter.rx.val[k][tap] = 0;
421   
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);
430     break;
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);
437     break;
438   case ADC_REAL:
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);
442     break;
443   case ADC_IMAG:
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);
447     break;
448   case AGC_GAIN:
449     uni.meter.rx.val[k][tap] = 20.0 * log10(rx[k].agc.gen->gain.now + 1e-80);
450     break;
451   default:
452     break;
453   }
454 }
455
456 PRIVATE void
457 do_tx_meter(CXB buf, int tap) {
458   COMPLEX *vec = CXBbase(buf);
459   int i, len = CXBhave(buf);
460   
461   uni.meter.tx.val[tap] = 0;
462
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);
470     break;
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);
477     break;
478   case ALC:
479     {
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));
483     }
484     break;
485   case PWR:
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] /= uni.buflen;
489     break;
490   case PKPWR:
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)));
494     break;
495   default:
496     break;
497   }
498 }
499
500 PRIVATE void
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;
507   }
508 }
509
510 PRIVATE void
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;
516 }
517
518 //========================================================================
519 /* RX processing */ 
520
521 PRIVATE BOOLEAN
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;
526
527     for (i = 0; i < n; i++)
528       rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i));
529
530     return
531       10.0 * log10(rx[k].squelch.power) < rx[k].squelch.thresh;
532
533   } else
534     return rx[k].squelch.set = FALSE;
535 }
536
537 // apply squelch
538 // slew into silence first time
539
540 PRIVATE void
541 do_squelch(int k) {
542   rx[k].squelch.set = TRUE;
543   
544   if (!rx[k].squelch.running) {
545     int i,
546         m = rx[k].squelch.num,
547         n = CXBhave(rx[k].buf.o) - m;
548
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);
552   
553     memset((void *) (CXBbase(rx[k].buf.o) + m),
554            0,
555            n * sizeof(COMPLEX));
556     rx[k].squelch.running = TRUE;
557
558   } else
559     memset((void *) CXBbase(rx[k].buf.o),
560            0,
561            CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
562 }
563
564 // lift squelch
565 // slew out from silence to full scale
566
567 PRIVATE void
568 no_squelch(int k) {
569   if (rx[k].squelch.running) {
570     int i, m = rx[k].squelch.num;
571
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);
575
576     rx[k].squelch.running = FALSE;
577   }
578 }
579
580 /* pre-condition for (nearly) all RX modes */
581
582 PRIVATE void
583 do_rx_pre(int k) {
584   int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
585
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),
589                                      rx[k].scl.pre.val); 
590
591   if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen);
592   if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen);
593
594   // metering for uncorrected values here
595
596   do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_CONV);
597
598   correctIQ(rx[k].buf.i, rx[k].iqfix);
599
600   /* 2nd IF conversion happens here */
601
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));
607   } 
608
609   /* filtering, metering, spectrum, squelch, & AGC */
610   
611   if (rx[k].mode == SPEC)
612     
613     do_rx_spectrum(k, rx[k].buf.i, SPEC_SEMI_RAW);
614   
615   else {
616     
617     do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_FILT);
618     do_rx_spectrum(k, rx[k].buf.i, SPEC_PRE_FILT);
619     
620     if (rx[k].tick == 0)
621       reset_OvSv(rx[k].filt.ovsv);
622     
623     filter_OvSv(rx[k].filt.ovsv);
624     CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i);
625     
626     do_rx_meter(k, rx[k].buf.o, RXMETER_POST_FILT);
627     do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_FILT);
628     
629     if (rx[k].cpd.flag)
630       WSCompand(rx[k].cpd.gen);
631
632     if (should_do_rx_squelch(k))
633       do_squelch(k);
634     
635     else if (rx[k].agc.flag)
636       DigitalAgc(rx[k].agc.gen, rx[k].tick);
637
638     do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_AGC);
639   }
640 }
641
642 PRIVATE void
643 do_rx_post(int k) {
644   int i, n = CXBhave(rx[k].buf.o);
645   
646   if (!rx[k].squelch.set)  {
647     no_squelch(k);
648     // spotting tone
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));
655     }
656   }
657   
658   // final scaling
659   
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),
663                                      rx[k].scl.post.val);
664   
665   // not binaural?
666   // position in stereo field
667   
668   if (!rx[k].bin.flag)
669     for (i = 0; i < n; i++)
670       CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i));
671 }
672
673 /* demod processing */
674
675 PRIVATE void
676 do_rx_SBCW(int k) {
677   if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen);
678   if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen);
679 }
680
681 PRIVATE void
682 do_rx_AM(int k) { AMDemod(rx[k].am.gen); }
683
684 PRIVATE void
685 do_rx_FM(int k) { FMDemod(rx[k].fm.gen); }
686
687 PRIVATE void
688 do_rx_DRM(int k) {}
689
690 PRIVATE void
691 do_rx_SPEC(int k) {
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);
696 }
697
698 PRIVATE void
699 do_rx_NIL(int k) {
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;
702 }
703
704 /* overall dispatch for RX processing */
705
706 PRIVATE void
707 do_rx(int k) {
708   do_rx_pre(k);
709   switch (rx[k].mode) {
710   case USB:
711   case LSB:
712   case CWU:
713   case CWL:
714   case DSB:  do_rx_SBCW(k); break;
715   case AM:
716   case SAM:  do_rx_AM(k); break;
717   case FMN:  do_rx_FM(k);   break;
718   case DRM:  do_rx_DRM(k);  break;
719   case SPEC:
720     default: do_rx_SPEC(k); break;
721   }
722   do_rx_post(k);
723 }  
724
725 //==============================================================
726 /* TX processing */
727
728 /* pre-condition for (nearly) all TX modes */
729
730 PRIVATE void
731 do_tx_pre(void) {
732
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);
737   }
738
739   correctIQ(tx.buf.i, tx.iqfix);
740
741   if (tx.dcb.flag) DCBlock(tx.dcb.gen);
742
743   if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
744   if (tx.cpd.flag) WSCompand(tx.cpd.gen);
745
746   if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
747   filter_OvSv(tx.filt.ovsv);
748
749 }
750
751 PRIVATE void
752 do_tx_post(void) {
753   CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
754
755   if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
756
757   // meter modulated signal
758
759   do_tx_meter(tx.buf.o, TXMETER_POST_MOD);
760
761   if (tx.scl.post.flag) {
762     int i, n = CXBhave(tx.buf.o);
763     for (i = 0; i < n; i++)
764       CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
765   }
766
767   if (uni.spec.flag)
768     do_tx_spectrum(tx.buf.o);
769
770   if (tx.osc.gen->Frequency != 0.0) {
771     int i;
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));
775   }
776 }
777
778 /* modulator processing */
779
780 PRIVATE void
781 do_tx_SBCW(void) {
782   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
783
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);
789     }
790 }
791
792 PRIVATE void
793 do_tx_AM(void) {
794   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
795
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;
803     }
804 }
805
806 PRIVATE void
807 do_tx_FM(void) {
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);
818     }
819 }
820
821 PRIVATE void
822 do_tx_NIL(void) {
823   int i, n = min(CXBhave(tx.buf.i), uni.buflen);
824   for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
825 }
826
827 /* general TX processing dispatch */
828
829 PRIVATE void
830 do_tx(void) {
831   do_tx_pre();
832   switch (tx.mode) {
833   case USB:
834   case LSB:
835   case CWU:
836   case CWL:
837   case DSB:  do_tx_SBCW(); break;
838   case AM:
839   case SAM:  do_tx_AM();   break;
840   case FMN:  do_tx_FM();   break;
841   case DRM:
842   case SPEC:
843     default: do_tx_NIL(); break;
844   }
845   do_tx_post();
846 }
847
848 //========================================================================
849 /* overall buffer processing;
850    come here when there are buffers to work on */
851
852 void
853 process_samples(float *bufl, float *bufr,
854                 float *auxl, float *auxr,
855                 int n) {
856   int i, k;
857   
858   switch (uni.mode.trx) {
859     
860   case RX:
861     
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;
868       }
869
870     // prepare buffers for mixing
871     memset((char *) bufl, 0, n * sizeof(float));
872     memset((char *) bufr, 0, n * sizeof(float));
873
874     // run all receivers
875     for (k = 0; k < uni.multirx.nrx; k++)
876       if (uni.multirx.act[k]) {
877         do_rx(k), rx[k].tick++;
878         // mix
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;
883       }
884
885     // late mixing of aux buffers
886     if (uni.mix.rx.flag)
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);
890
891     break;
892
893   case TX:
894
895     // early mixing of aux buffers
896     if (uni.mix.tx.flag)
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);
900
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;
904
905     do_tx(), tx.tick++;
906
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;
910
911     break;
912   }
913
914   uni.tick++;
915 }