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