]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/sdr.c
aad64d3f50f12469f2e0ff4ace01dce398084646
[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.tick = 0;
119 }
120
121 /* purely rx */
122
123 PRIVATE void
124 setup_rx(int k) {
125   
126   /* conditioning */
127   rx[k].iqfix = newCorrectIQ(0.0, 1.0);
128   rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
129                                             4800.0,
130                                             uni.samplerate,
131                                             uni.buflen + 1);
132   rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef),
133                                 FIRsize(rx[k].filt.coef),
134                                 uni.wisdom.bits);
135   normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec,
136                         rx[k].filt.ovsv->fftlen);
137
138   // hack for EQ
139   rx[k].filt.save = newvec_COMPLEX(rx[k].filt.ovsv->fftlen, "RX filter cache");
140   memcpy((char *) rx[k].filt.save,
141          (char *) rx[k].filt.ovsv->zfvec,
142          rx[k].filt.ovsv->fftlen * sizeof(COMPLEX));
143
144   /* buffers */
145   /* note we overload the internal filter buffers
146      we just created */
147   rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv),
148                        FiltOvSv_fetchpoint(rx[k].filt.ovsv),
149                        "init rx.buf.i");
150   rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv),
151                        FiltOvSv_storepoint(rx[k].filt.ovsv),
152                        "init rx[k].buf.o");
153   
154   /* conversion */
155   rx[k].osc.freq = -11025.0;
156   rx[k].osc.phase = 0.0;
157   rx[k].osc.gen = newOSC(uni.buflen,
158                          ComplexTone,
159                          rx[k].osc.freq,
160                          rx[k].osc.phase,
161                          uni.samplerate,
162                          "SDR RX Oscillator");
163
164   rx[k].agc.gen = newDigitalAgc(agcMED, // Mode
165                              7,         // Hang
166                              7,         // Size
167                              48,        // Ramp
168                              3,         // Over
169                              3,         // Rcov
170                              CXBsize(rx[k].buf.o),      // BufSize
171                              100.0,     // MaxGain
172                              0.707,     // Limit
173                              1.0,       // CurGain
174                              CXBbase(rx[k].buf.o));
175   rx[k].agc.flag = TRUE;
176
177   /* demods */
178   rx[k].am.gen = newAMD(48000.0,        // REAL samprate
179                         0.0,    // REAL f_initial
180                         -500.0, // REAL f_lobound,
181                         500.0,  // REAL f_hibound,
182                         400.0,  // REAL f_bandwid,
183                         CXBsize(rx[k].buf.o),   // int size,
184                         CXBbase(rx[k].buf.o),   // COMPLEX *ivec,
185                         CXBbase(rx[k].buf.o),   // COMPLEX *ovec,
186                         AMdet,  // AM Mode AMdet == rectifier,
187                                 //         SAMdet == synchronous detector
188                         "AM detector blew");    // char *tag
189   rx[k].fm.gen = newFMD(48000,  // REAL samprate
190                         0.0,    // REAL f_initial
191                         -6000.0,        // REAL f_lobound
192                         6000.0, // REAL f_hibound
193                         10000.0,        // REAL f_bandwid
194                         CXBsize(rx[k].buf.o),   // int size
195                         CXBbase(rx[k].buf.o),   // COMPLEX *ivec
196                         CXBbase(rx[k].buf.o),   // COMPLEX *ovec
197                         "New FM Demod structure");      // char *error message;
198
199   /* noise reduction */
200   rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal,
201                            64,          // int delay,
202                            0.01,                // REAL adaptation_rate,
203                            0.00001,     // REAL leakage,
204                            45,          // int adaptive_filter_size,
205                            LMADF_INTERFERENCE);
206   rx[k].anf.flag = FALSE;
207   rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal,
208                            64,          // int delay,
209                            0.01,                // REAL adaptation_rate,
210                            0.00001,     // REAL leakage,
211                            45,          // int adaptive_filter_size,
212                            LMADF_NOISE);
213   rx[k].anr.flag = FALSE;
214
215   rx[k].nb.thresh = 3.3;
216   rx[k].nb.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb.thresh);
217   rx[k].nb.flag = FALSE;
218
219   rx[k].nb_sdrom.thresh = 2.5;
220   rx[k].nb_sdrom.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb_sdrom.thresh);
221   rx[k].nb_sdrom.flag = FALSE;
222
223   rx[k].spot.gen = newSpotToneGen(-12.0,        // gain
224                                   700.0,        // freq
225                                   5.0,  // ms rise
226                                   5.0,  // ms fall
227                                   uni.buflen,
228                                   uni.samplerate);
229
230   rx[k].scl.pre.val = 1.0;
231   rx[k].scl.pre.flag = FALSE;
232   rx[k].scl.post.val = 1.0;
233   rx[k].scl.post.flag = FALSE;
234
235   memset((char *) &rx[k].squelch, 0, sizeof(rx[k].squelch));
236   rx[k].squelch.thresh = -30.0;
237   rx[k].squelch.power = 0.0;
238   rx[k].squelch.flag = rx[k].squelch.running = rx[k].squelch.set = FALSE;
239   rx[k].squelch.num = (int) (0.0395 * uni.samplerate + 0.5);
240
241   rx[k].mode = uni.mode.sdr;
242   rx[k].bin.flag = FALSE;
243
244   {
245     REAL pos = 0.5, // 0 <= pos <= 1, left->right
246          theta = (1.0 - pos) * M_PI / 2.0;
247     rx[k].azim = Cmplx(cos(theta), sin(theta));
248   }
249
250   rx[k].tick = 0;
251 }
252
253 /* purely tx */
254
255 PRIVATE void
256 setup_tx(void) {
257
258   /* conditioning */
259   tx.iqfix = newCorrectIQ(0.0, 1.0);
260   tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
261                                          3000.0,
262                                          uni.samplerate,
263                                          uni.buflen + 1);
264   tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
265                              FIRsize(tx.filt.coef),
266                              uni.wisdom.bits);
267   normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
268                         tx.filt.ovsv->fftlen);
269
270   // hack for EQ
271   tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache");
272   memcpy((char *) tx.filt.save,
273          (char *) tx.filt.ovsv->zfvec,
274          tx.filt.ovsv->fftlen * sizeof(COMPLEX));
275
276   /* buffers */
277   tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
278                     FiltOvSv_fetchpoint(tx.filt.ovsv),
279                     "init tx.buf.i");
280   tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
281                     FiltOvSv_storepoint(tx.filt.ovsv),
282                     "init tx.buf.o");
283   
284   /* conversion */
285   tx.osc.freq = 0.0;
286   tx.osc.phase = 0.0;
287   tx.osc.gen = newOSC(uni.buflen,
288                       ComplexTone,
289                       tx.osc.freq,
290                       tx.osc.phase,
291                       uni.samplerate,
292                       "SDR TX Oscillator");
293
294   tx.agc.gen = newDigitalAgc(agcFAST,   // Mode
295                              3,         // Hang
296                              3,         // Size
297                              3,         // Over
298                              3,         // Rcov
299                              48,        // Ramp
300                              CXBsize(tx.buf.o), // BufSize
301                              1.0,       // MaxGain
302                              0.900,     // Limit
303                              1.0,       // CurGain
304                              CXBbase(tx.buf.o));
305   tx.agc.flag = TRUE;
306
307   tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i));
308   tx.spr.flag = FALSE;
309
310   tx.scl.dc = cxzero;
311   tx.scl.pre.val = 1.0;
312   tx.scl.pre.flag = FALSE;
313   tx.scl.post.val = 1.0;
314   tx.scl.post.flag = FALSE;
315
316   tx.mode = uni.mode.sdr;
317
318   tx.tick = 0;
319   /* not much else to do for TX */
320 }
321
322 /* how the outside world sees it */
323
324 void
325 setup_workspace(void) {
326   int k;
327
328   setup_all();
329
330   for (k = 0; k < uni.multirx.nrx; k++) {
331     setup_rx(k);
332     uni.multirx.act[k] = FALSE;
333   }
334   uni.multirx.act[0] = TRUE;
335   uni.multirx.nac = 1;
336   
337   setup_tx();
338 }
339
340 void
341 destroy_workspace(void) {
342   int k;
343
344   /* TX */
345   delSpeechProc(tx.spr.gen);
346   delDigitalAgc(tx.agc.gen);
347   delOSC(tx.osc.gen);
348   delvec_COMPLEX(tx.filt.save);
349   delFiltOvSv(tx.filt.ovsv);
350   delFIR_Bandpass_COMPLEX(tx.filt.coef);
351   delCorrectIQ(tx.iqfix);
352   delCXB(tx.buf.o);
353   delCXB(tx.buf.i);
354
355   /* RX */
356   for (k = 0; k < uni.multirx.nrx; k++) {
357     delSpotToneGen(rx[k].spot.gen);
358     delDigitalAgc(rx[k].agc.gen);
359     del_nb(rx[k].nb_sdrom.gen);
360     del_nb(rx[k].nb.gen);
361     del_lmsr(rx[k].anf.gen);
362     del_lmsr(rx[k].anr.gen);
363     delAMD(rx[k].am.gen);
364     delFMD(rx[k].fm.gen);
365     delOSC(rx[k].osc.gen);
366     delvec_COMPLEX(rx[k].filt.save);
367     delFiltOvSv(rx[k].filt.ovsv);
368     delFIR_Bandpass_COMPLEX(rx[k].filt.coef);
369     delCorrectIQ(rx[k].iqfix);
370     delCXB(rx[k].buf.o);
371     delCXB(rx[k].buf.i);
372   }
373   
374   /* all */
375   finish_spectrum(&uni.spec);
376 }
377
378 //////////////////////////////////////////////////////////////////////////
379 // execution
380 //////////////////////////////////////////////////////////////////////////
381
382 //========================================================================
383 // util
384
385 PRIVATE REAL
386 CXBnorm(CXB buff) {
387   int i;
388   double sum = 0.0;
389   for (i = 0; i < CXBhave(buff); i++)
390     sum += Csqrmag(CXBdata(buff, i));
391   return sqrt(sum);
392 }
393
394 //========================================================================
395 /* all */
396
397 // unfortunate duplication here, due to
398 // multirx vs monotx
399
400 PRIVATE void
401 do_rx_meter(int k, CXB buf, int tap) {
402   COMPLEX *vec = CXBbase(buf);
403   int i, len = CXBhave(buf);
404   
405   uni.meter.rx.val[k][tap] = 0;
406   
407   switch (uni.meter.rx.type) {
408   case AVG_SIGNAL_STRENGTH:
409     for (i = 0; i < len; i++)
410       uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
411     uni.meter.rx.val[k][tap] =
412       uni.meter.rx.avg[k][tap] =
413         0.9 * uni.meter.rx.avg[k][tap] + log10(uni.meter.rx.val[k][tap] + 1e-20);
414     break;
415   case SIGNAL_STRENGTH:
416     for (i = 0; i < len; i++)
417       uni.meter.rx.val[k][tap] += Csqrmag(vec[i]);
418     uni.meter.rx.avg[k][tap] =
419       uni.meter.rx.val[k][tap] =
420         10.0 * log10(uni.meter.rx.val[k][tap] + 1e-20);
421     break;
422   case ADC_REAL:
423     for(i = 0; i < len; i++)
424       uni.meter.rx.val[k][tap] = max(fabs(vec[i].re), uni.meter.rx.val[k][tap]);
425     uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10);
426     break;
427   case ADC_IMAG:
428     for(i = 0; i < len; i++)
429       uni.meter.rx.val[k][tap] = max(fabs(vec[i].im), uni.meter.rx.val[k][tap]);
430     uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10);
431     break;
432   default:
433     break;
434   }
435 }
436
437 PRIVATE void
438 do_tx_meter(CXB buf, int tap) {
439   COMPLEX *vec = CXBbase(buf);
440   int i, len = CXBhave(buf);
441   
442   uni.meter.tx.val[tap] = 0;
443
444   switch (uni.meter.tx.type) {
445   case AVG_SIGNAL_STRENGTH:
446     for (i = 0; i < len; i++)
447       uni.meter.tx.val[tap] += Csqrmag(vec[i]);
448     uni.meter.tx.val[tap] =
449       uni.meter.tx.avg[tap] =
450         0.9 * uni.meter.tx.avg[tap] + log10(uni.meter.tx.val[tap] + 1e-20);
451     break;
452   case SIGNAL_STRENGTH:
453     for (i = 0; i < len; i++)
454       uni.meter.tx.val[tap] += Csqrmag(vec[i]);
455     uni.meter.tx.avg[tap] =
456       uni.meter.tx.val[tap] =
457         10.0 * log10(uni.meter.tx.val[tap] + 1e-20);
458     break;
459   case ADC_REAL:
460     for(i = 0; i < len; i++)
461       uni.meter.tx.val[tap] = max(fabs(vec[i].re), uni.meter.tx.val[tap]);
462     uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10);
463     break;
464   case ADC_IMAG:
465     for(i = 0; i < len; i++)
466       uni.meter.tx.val[tap] = max(fabs(vec[i].im), uni.meter.tx.val[tap]);
467     uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10);
468     break;
469   default:
470     break;
471   }
472 }
473
474 PRIVATE BOOLEAN
475 do_rx_spectrum(int k, CXB buf, int type) {
476   if (uni.spec.flag && k == uni.spec.rxk && type == uni.spec.type) {
477     memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
478            (char *) CXBbase(buf),
479            CXBhave(buf)); 
480     uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size;
481   }
482 }
483
484 PRIVATE void
485 do_tx_spectrum(CXB buf) {
486   memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill),
487          (char *) CXBbase(buf),
488          CXBhave(buf));
489   uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size;
490 }
491
492 //========================================================================
493 /* RX processing */ 
494
495 PRIVATE BOOLEAN
496 should_do_rx_squelch(int k) {
497   if (rx[k].squelch.flag) {
498     int i, n = CXBhave(rx[k].buf.o);
499     rx[k].squelch.power = 0.0;
500     for (i = 0; i < n; i++)
501       rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i));
502     return rx[k].squelch.thresh > 10.0 * log10(rx[k].squelch.power);
503   } else
504     return rx[k].squelch.set = FALSE;
505 }
506
507 // apply squelch
508 // slew into silence first time
509
510 PRIVATE void
511 do_squelch(int k) {
512   rx[k].squelch.set = TRUE;
513   if (!rx[k].squelch.running) {
514     int i, m = rx[k].squelch.num, n = CXBhave(rx[k].buf.o) - m;
515     for (i = 0; i < m; i++)
516       CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m);
517     memset((void *) (CXBbase(rx[k].buf.o) + m), 0, n * sizeof(COMPLEX));
518     rx[k].squelch.running = TRUE;
519   } else
520     memset((void *) CXBbase(rx[k].buf.o), 0, CXBhave(rx[k].buf.o) * sizeof(COMPLEX));
521 }
522
523 // lift squelch
524 // slew out from silence to full scale
525
526 PRIVATE void
527 no_squelch(int k) {
528   if (rx[k].squelch.running) {
529     int i, m = rx[k].squelch.num;
530     for (i = 0; i < m; i++)
531       CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m);
532     rx[k].squelch.running = FALSE;
533   }
534 }
535
536 /* pre-condition for (nearly) all RX modes */
537
538 PRIVATE void
539 do_rx_pre(int k) {
540   int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
541
542   if (rx[k].scl.pre.flag)
543     for (i = 0; i < n; i++)
544       CXBdata(rx[k].buf.i, i) = Cscl(CXBdata(rx[k].buf.i, i),
545                                      rx[k].scl.pre.val); 
546
547   if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen);
548   if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen);
549
550   // metering for uncorrected values here
551
552   do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_CONV);
553
554   correctIQ(rx[k].buf.i, rx[k].iqfix);
555
556   /* 2nd IF conversion happens here */
557
558   if (rx[k].osc.gen->Frequency != 0.0) {
559     ComplexOSC(rx[k].osc.gen);
560     for (i = 0; i < n; i++)
561       CXBdata(rx[k].buf.i, i) = Cmul(CXBdata(rx[k].buf.i, i),
562                                      OSCCdata(rx[k].osc.gen, i));
563   } 
564
565   /* filtering, metering, spectrum, squelch, & AGC */
566   
567   if (rx[k].mode == SPEC)
568     
569     do_rx_spectrum(k, rx[k].buf.i, SPEC_SEMI_RAW);
570   
571   else {
572     
573     do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_FILT);
574     do_rx_spectrum(k, rx[k].buf.i, SPEC_PRE_FILT);
575     
576     if (rx[k].tick == 0)
577       reset_OvSv(rx[k].filt.ovsv);
578     
579     filter_OvSv(rx[k].filt.ovsv);
580     CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i);
581     
582     do_rx_meter(k, rx[k].buf.o, RXMETER_POST_FILT);
583     do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_FILT);
584     
585     if (should_do_rx_squelch(k))
586       do_squelch(k);
587     
588     else if (rx[k].agc.flag)
589       DigitalAgc(rx[k].agc.gen, rx[k].tick);
590     
591   }
592 }
593
594 PRIVATE void
595 do_rx_post(int k) {
596   int i, n = CXBhave(rx[k].buf.o);
597   
598   if (!rx[k].squelch.set)  {
599     no_squelch(k);
600     // spotting tone
601     if (rx[k].spot.flag) {
602       // remember whether it's turned itself off during this pass
603       rx[k].spot.flag = SpotTone(rx[k].spot.gen);
604       for (i = 0; i < n; i++)
605         CXBdata(rx[k].buf.o, i) = Cadd(CXBdata(rx[k].buf.o, i),
606                                        CXBdata(rx[k].spot.gen->buf, i));
607     }
608   }
609   
610   // final scaling
611   
612   if (rx[k].scl.post.flag)
613     for (i = 0; i < n; i++)
614       CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i),
615                                      rx[k].scl.post.val);
616   
617   // not binaural?
618   // position in stereo field
619   
620   if (!rx[k].bin.flag)
621     for (i = 0; i < n; i++)
622       CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i));
623 }
624
625 /* demod processing */
626
627 PRIVATE void
628 do_rx_SBCW(int k) {
629   if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen);
630   if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen);
631 }
632
633 PRIVATE void
634 do_rx_AM(int k) { AMDemod(rx[k].am.gen); }
635
636 PRIVATE void
637 do_rx_FM(int k) { FMDemod(rx[k].fm.gen); }
638
639 PRIVATE void
640 do_rx_DRM(int k) {}
641
642 PRIVATE void
643 do_rx_SPEC(int k) {
644   memcpy(CXBbase(rx[k].buf.o),
645          CXBbase(rx[k].buf.i),
646          sizeof(COMPLEX) * CXBhave(rx[k].buf.i));
647   if (rx[k].agc.flag) DigitalAgc(rx[k].agc.gen, rx[k].tick);
648 }
649
650 PRIVATE void
651 do_rx_NIL(int k) {
652   int i, n = min(CXBhave(rx[k].buf.i), uni.buflen);
653   for (i = 0; i < n; i++) CXBdata(rx[k].buf.o, i) = cxzero;
654 }
655
656 /* overall dispatch for RX processing */
657
658 PRIVATE void
659 do_rx(int k) {
660   do_rx_pre(k);
661   switch (rx[k].mode) {
662   case USB:
663   case LSB:
664   case CWU:
665   case CWL:
666   case DSB:  do_rx_SBCW(k); break;
667   case AM:
668   case SAM:  do_rx_AM(k); break;
669   case FMN:  do_rx_FM(k);   break;
670   case DRM:  do_rx_DRM(k);  break;
671   case SPEC:
672     default: do_rx_SPEC(k); break;
673   }
674   do_rx_post(k);
675 }  
676
677 //==============================================================
678 /* TX processing */
679
680 /* pre-condition for (nearly) all TX modes */
681
682 PRIVATE void
683 do_tx_pre(void) {
684
685 if (tx.scl.pre.flag) {
686 int i, n = CXBhave(tx.buf.i);
687     for (i = 0; i < n; i++)
688       CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0);
689   }
690
691   correctIQ(tx.buf.i, tx.iqfix);
692
693   if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
694
695   if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
696   filter_OvSv(tx.filt.ovsv);
697 }
698
699 PRIVATE void
700 do_tx_post(void) {
701   CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
702
703   if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
704
705   // meter modulated signal
706
707   do_tx_meter(tx.buf.o, TXMETER_POST_MOD);
708
709   if (tx.scl.post.flag) {
710     int i, n = CXBhave(tx.buf.o);
711     for (i = 0; i < n; i++)
712       CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
713   }
714
715   if (uni.spec.flag)
716     do_tx_spectrum(tx.buf.o);
717
718   if (tx.osc.gen->Frequency != 0.0) {
719     int i;
720     ComplexOSC(tx.osc.gen);
721     for (i = 0; i < CXBhave(tx.buf.o); i++)
722       CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i));
723   }
724 }
725
726 /* modulator processing */
727
728 PRIVATE void
729 do_tx_SBCW(void) {
730   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
731
732   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
733     for (i = 0; i < n; i++) {
734       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99),
735                        Cscl(CXBdata(tx.buf.o, i), -0.01));
736       CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc);
737     }
738 }
739
740 PRIVATE void
741 do_tx_AM(void) {
742   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
743
744   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
745     for (i = 0; i < n; i++) { 
746       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
747                        Cscl(CXBdata(tx.buf.o, i), -0.001));
748       CXBreal(tx.buf.o, i) =
749         0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re);
750       CXBimag(tx.buf.o, i) = 0.0;
751     }
752 }
753
754 PRIVATE void
755 do_tx_FM(void) {
756   int i, n = min(CXBhave(tx.buf.o), uni.buflen);
757   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
758     for (i = 0; i < n; i++) {
759       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
760                        Cscl(CXBdata(tx.buf.o, i), 0.001));
761       tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq;
762       if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI;
763       if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI;
764       CXBdata(tx.buf.o, i) =
765         Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999);
766     }
767 }
768
769 PRIVATE void
770 do_tx_NIL(void) {
771   int i, n = min(CXBhave(tx.buf.i), uni.buflen);
772   for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
773 }
774
775 /* general TX processing dispatch */
776
777 PRIVATE void
778 do_tx(void) {
779   do_tx_pre();
780   switch (tx.mode) {
781   case USB:
782   case LSB:
783   case CWU:
784   case CWL:
785   case DSB:  do_tx_SBCW(); break;
786   case AM:
787   case SAM:  do_tx_AM();   break;
788   case FMN:  do_tx_FM();   break;
789   case DRM:
790   case SPEC:
791     default: do_tx_NIL(); break;
792   }
793   do_tx_post();
794 }
795
796 //========================================================================
797 /* overall buffer processing;
798    come here when there are buffers to work on */
799
800 void
801 process_samples(float *bufl, float *bufr,
802                 float *auxl, float *auxr,
803                 int n) {
804   int i, k;
805   
806   switch (uni.mode.trx) {
807     
808   case RX:
809     
810     // make copies of the input for all receivers
811     for (k = 0; k < uni.multirx.nrx; k++)
812       if (uni.multirx.act[k]) {
813         for (i = 0; i < n; i++)
814           CXBimag(rx[k].buf.i, i) = bufl[i], CXBreal(rx[k].buf.i, i) = bufr[i];
815         CXBhave(rx[k].buf.i) = n;
816       }
817
818     // prepare buffers for mixing
819     memset((char *) bufl, 0, n * sizeof(float));
820     memset((char *) bufr, 0, n * sizeof(float));
821
822     // run all receivers
823     for (k = 0; k < uni.multirx.nrx; k++)
824       if (uni.multirx.act[k]) {
825         do_rx(k), rx[k].tick++;
826         // mix
827         for (i = 0; i < n; i++)
828           bufl[i] += CXBimag(rx[k].buf.o, i),
829           bufr[i] += CXBreal(rx[k].buf.o, i);
830         CXBhave(rx[k].buf.o) = n;
831       }
832
833     // late mixing of aux buffers
834     if (uni.mix.rx.flag)
835       for (i = 0; i < n; i++)
836         bufl[i] += auxl[i] * uni.mix.rx.gain,
837         bufr[i] += auxr[i] * uni.mix.rx.gain;
838
839     break;
840
841   case TX:
842
843     // early mixing of aux buffers
844     if (uni.mix.tx.flag)
845       for (i = 0; i < n; i++)
846         bufl[i] += auxl[i] * uni.mix.tx.gain,
847         bufr[i] += auxr[i] * uni.mix.tx.gain;
848
849     for (i = 0; i < n; i++)
850       CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
851     CXBhave(tx.buf.i) = n;
852
853     do_tx(), tx.tick++;
854
855     for (i = 0; i < n; i++)
856       bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i);
857     CXBhave(tx.buf.o) = n;
858
859     break;
860   }
861
862   uni.tick++;
863 }