]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/sdr.c
changes to speechproc, correct IQ -- restore squelch in sdr.c -- other minor stuff
[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 /* global and general info,
40    not specifically attached to
41    tx, rx, or scheduling */
42
43 PRIVATE void
44 setup_all(void) {
45
46   uni.samplerate = loc.def.rate;
47   uni.buflen = loc.def.size;
48   uni.mode.sdr = loc.def.mode;
49   uni.mode.trx = RX;
50
51   if (uni.meter.flag) {
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);
56   }
57
58   uni.wisdom.path = loc.path.wisdom;
59   uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE;
60   {
61     FILE *f = fopen(uni.wisdom.path, "r");
62     if (f) {
63 #define WBUFLEN 2048
64 #define WSTRLEN 64      
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;
70 #undef WSTRLEN
71 #undef WBUFLEN      
72       fclose(f);
73     }
74   }
75
76   uni.tick = 0;
77 }
78
79 /* purely rx */
80
81 PRIVATE void
82 setup_rx(void) {
83
84   /* conditioning */
85   rx.iqfix = newCorrectIQ(0.0, 1.0);
86   rx.filt.coef = newFIR_Bandpass_COMPLEX(-4800.0,
87                                          4800.0,
88                                          uni.samplerate,
89                                          uni.buflen + 1);
90   rx.filt.ovsv = newFiltOvSv(FIRcoef(rx.filt.coef),
91                              FIRsize(rx.filt.coef),
92                              uni.wisdom.bits);
93   normalize_vec_COMPLEX(rx.filt.ovsv->zfvec,
94                         rx.filt.ovsv->fftlen);
95
96   // hack for EQ
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));
101
102   /* buffers */
103   /* note we overload the internal filter buffers
104      we just created */
105   rx.buf.i = newCXB(FiltOvSv_fetchsize(rx.filt.ovsv),
106                     FiltOvSv_fetchpoint(rx.filt.ovsv),
107                     "init rx.buf.i");
108   rx.buf.o = newCXB(FiltOvSv_storesize(rx.filt.ovsv),
109                     FiltOvSv_storepoint(rx.filt.ovsv),
110                     "init rx.buf.o");
111   
112   /* conversion */
113   rx.osc.freq = -11025.0;
114   rx.osc.phase = 0.0;
115   rx.osc.gen = newOSC(uni.buflen,
116                       ComplexTone,
117                       rx.osc.freq,
118                       rx.osc.phase,
119                       uni.samplerate,
120                       "SDR RX Oscillator");
121
122   rx.agc.gen = newDigitalAgc(agcMED,    // Mode
123                              7,         // Hang
124                              7,         // Size
125                              48,        // Ramp
126                              3,         // Over
127                              3,         // Rcov
128                              CXBsize(rx.buf.o), // BufSize
129                              100.0,     // MaxGain
130                              0.707,     // Limit
131                              1.0,       // CurGain
132                              CXBbase(rx.buf.o));
133   rx.agc.flag = TRUE;
134
135   /* demods */
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;
156
157   /* noise reduction */
158   rx.anf.gen = new_lmsr(rx.buf.o,       // CXB signal,
159                         64,             // int delay,
160                         0.01,           // REAL adaptation_rate,
161                         0.00001,        // REAL leakage,
162                         45,             // int adaptive_filter_size,
163                         LMADF_INTERFERENCE);
164   rx.anf.flag = FALSE;
165   rx.anr.gen = new_lmsr(rx.buf.o,       // CXB signal,
166                         64,             // int delay,
167                         0.01,           // REAL adaptation_rate,
168                         0.00001,        // REAL leakage,
169                         45,             // int adaptive_filter_size,
170                         LMADF_NOISE);
171   rx.anr.flag = FALSE;
172
173   rx.nb.thresh = 3.3;
174   rx.nb.gen = new_noiseblanker(rx.buf.i, rx.nb.thresh);
175   rx.nb.flag = FALSE;
176
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;
180
181   rx.spot.gen = newSpotToneGen(-12.0,   // gain
182                                700.0,   // freq
183                                5.0,     // ms rise
184                                5.0,     // ms fall
185                                uni.buflen,
186                                uni.samplerate);
187
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;
192
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);
198
199   rx.mode = uni.mode.sdr;
200   rx.bin.flag = FALSE;
201
202   rx.tick = 0;
203 }
204
205 /* purely tx */
206
207 PRIVATE void
208 setup_tx(void) {
209
210   /* conditioning */
211   tx.iqfix = newCorrectIQ(0.0, 1.0);
212   tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0,
213                                          3000.0,
214                                          uni.samplerate,
215                                          uni.buflen + 1);
216   tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef),
217                              FIRsize(tx.filt.coef),
218                              uni.wisdom.bits);
219   normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
220                         tx.filt.ovsv->fftlen);
221
222   // hack for EQ
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));
227
228   /* buffers */
229   tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv),
230                     FiltOvSv_fetchpoint(tx.filt.ovsv),
231                     "init tx.buf.i");
232   tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv),
233                     FiltOvSv_storepoint(tx.filt.ovsv),
234                     "init tx.buf.o");
235   
236   /* conversion */
237   tx.osc.freq = 0.0;
238   tx.osc.phase = 0.0;
239   tx.osc.gen = newOSC(uni.buflen,
240                       ComplexTone,
241                       tx.osc.freq,
242                       tx.osc.phase,
243                       uni.samplerate,
244                       "SDR TX Oscillator");
245
246   tx.agc.gen = newDigitalAgc(agcFAST,   // Mode
247                              3,         // Hang
248                              3,         // Size
249                              3,         // Over
250                              3,         // Rcov
251                              48,        // Ramp
252                              CXBsize(tx.buf.o), // BufSize
253                              1.0,       // MaxGain
254                              0.900,     // Limit
255                              1.0,       // CurGain
256                              CXBbase(tx.buf.o));
257   tx.agc.flag = TRUE;
258
259   tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i));
260   tx.spr.flag = FALSE;
261
262   tx.scl.dc = cxzero;
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;
267
268   tx.mode = uni.mode.sdr;
269
270   tx.tick = 0;
271   /* not much else to do for TX */
272 }
273
274 /* how the outside world sees it */
275
276 void
277 setup_workspace(void) {
278   setup_all(), setup_rx(), setup_tx();
279 }
280
281 void
282 destroy_workspace(void) {
283
284   /* TX */
285   delSpeechProc(tx.spr.gen);
286   delDigitalAgc(tx.agc.gen);
287   delOSC(tx.osc.gen);
288   delvec_COMPLEX(tx.filt.save);
289   delFiltOvSv(tx.filt.ovsv);
290   delFIR_Bandpass_COMPLEX(tx.filt.coef);
291   delCorrectIQ(tx.iqfix);
292   delCXB(tx.buf.o);
293   delCXB(tx.buf.i);
294
295   /* RX */
296   delSpotToneGen(rx.spot.gen);
297   delDigitalAgc(rx.agc.gen);
298   del_nb(rx.nb_sdrom.gen);
299   del_nb(rx.nb.gen);
300   del_lmsr(rx.anf.gen);
301   del_lmsr(rx.anr.gen);
302   delAMD(rx.am.gen);
303   delFMD(rx.fm.gen);
304   delOSC(rx.osc.gen);
305   delvec_COMPLEX(rx.filt.save);
306   delFiltOvSv(rx.filt.ovsv);
307   delFIR_Bandpass_COMPLEX(rx.filt.coef);
308   delCorrectIQ(rx.iqfix);
309   delCXB(rx.buf.o);
310   delCXB(rx.buf.i);
311
312   /* all */
313   if (uni.meter.flag)
314     closeChan(uni.meter.chan.c);
315 }
316
317 //////////////////////////////////////////////////////////////////////////
318 // execution
319 //////////////////////////////////////////////////////////////////////////
320
321 //========================================================================
322 // util
323
324 PRIVATE REAL
325 CXBnorm(CXB buff) {
326   int i;
327   double sum = 0.0;
328   for (i = 0; i < CXBhave(buff); i++)
329     sum += Csqrmag(CXBdata(buff, i));
330   return sqrt(sum);
331 }
332
333 //========================================================================
334 /* all */
335
336 /* tap off S-meter from some buf */
337
338 PRIVATE void
339 do_meter(COMPLEX *vec, int len) {
340   int i;
341   
342   uni.meter.val = 0;
343
344   switch (uni.meter.type) {
345   case AVG_SIGNAL_STRENGTH:
346     for (i = 0; i < len; i++)
347       uni.meter.val += Csqrmag(vec[i]);
348     uni.meter.val =
349       uni.meter.avgval = 0.9 * uni.meter.avgval + log10(uni.meter.val + 1e-20);
350     break;
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);
355     break;
356   case ADC_REAL:
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);
360     break;
361   case ADC_IMAG:
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);
365     break;
366   default:
367     break;
368   }
369
370   putChan_nowait(uni.meter.chan.c,
371                  (char *) &uni.meter.val,
372                  sizeof(uni.meter.val));
373 }
374
375 //========================================================================
376 /* RX processing */ 
377
378 PRIVATE BOOLEAN
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);
386   } else
387     return rx.squelch.set = FALSE;
388 }
389
390 // apply squelch
391 // slew into silence first time
392
393 PRIVATE void
394 do_squelch(void) {
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;
402   } else
403     memset((void *) CXBbase(rx.buf.o), 0, CXBhave(rx.buf.o) * sizeof(COMPLEX));
404 }
405
406 // lift squelch
407 // slew out from silence to full scale
408
409 PRIVATE void
410 no_squelch(void) {
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;
416   }
417 }
418
419 /* pre-condition for (nearly) all RX modes */
420
421 PRIVATE void
422 do_rx_pre(void) {
423   int i, n = min(CXBhave(rx.buf.i), uni.buflen);
424
425   //
426   // do shrinkage here
427   //
428
429   if (rx.scl.pre.flag)
430     for (i = 0; i < n; i++)
431       CXBdata(rx.buf.i, i) = Cscl(CXBdata(rx.buf.i, i),
432                                   rx.scl.pre.val); 
433
434   if (rx.nb.flag) noiseblanker(rx.nb.gen);
435   if (rx.nb_sdrom.flag) SDROMnoiseblanker(rx.nb_sdrom.gen);
436
437   correctIQ(rx.buf.i, rx.iqfix);
438
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));
445   } 
446
447   /* filtering, metering, squelch, & 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 (should_do_rx_squelch()) do_squelch();
454     if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick);
455   } else if (uni.meter.flag)
456     do_meter(CXBbase(rx.buf.o), uni.buflen);
457 }
458
459 PRIVATE void
460 do_rx_post(void) {
461   int i, n = CXBhave(rx.buf.o);
462
463   if (!rx.squelch.set)  {
464     no_squelch();
465     // spotting tone
466     if (rx.spot.flag) {
467       // remember whether it's turned itself off during this pass
468       rx.spot.flag = SpotTone(rx.spot.gen);
469       for (i = 0; i < n; i++)
470         CXBdata(rx.buf.o, i) = Cadd(CXBdata(rx.buf.o, i),
471                                     CXBdata(rx.spot.gen->buf, i));
472     }
473   }
474
475   //
476   // mix in sidetone here
477   //
478
479   if (rx.scl.post.flag)
480     for (i = 0; i < n; i++)
481       CXBdata(rx.buf.o, i) = Cscl(CXBdata(rx.buf.o, i),
482                                   rx.scl.post.val);
483
484   // not binaural? collapse
485   if (!rx.bin.flag)
486     for (i = 0; i < n; i++)
487       CXBimag(rx.buf.o, i) = CXBreal(rx.buf.o, i);
488 }
489
490 /* demod processing */
491
492 PRIVATE void
493 do_rx_SBCW(void) {
494   if (rx.anr.flag) lmsr_adapt(rx.anr.gen);
495   if (rx.anf.flag) lmsr_adapt(rx.anf.gen);
496 }
497
498 PRIVATE void
499 do_rx_AM(void) { AMDemod(rx.am.gen); }
500
501 PRIVATE void
502 do_rx_FM(void) { FMDemod(rx.fm.gen); }
503
504 PRIVATE void
505 do_rx_DRM(void) {}
506
507 PRIVATE void
508 do_rx_SPEC(void) {
509   memcpy(CXBbase(rx.buf.o),
510          CXBbase(rx.buf.i),
511          sizeof(COMPLEX) * CXBhave(rx.buf.i));
512   if (rx.agc.flag) DigitalAgc(rx.agc.gen, rx.tick);
513 }
514
515 PRIVATE void
516 do_rx_NIL(void) {
517   int i, n = min(CXBhave(rx.buf.i), uni.buflen);
518   for (i = 0; i < n; i++) CXBdata(rx.buf.o, i) = cxzero;
519 }
520
521 /* overall dispatch for RX processing */
522
523 PRIVATE void
524 do_rx(void) {
525   do_rx_pre();
526   switch (rx.mode) {
527   case USB:
528   case LSB:
529   case CWU:
530   case CWL:
531   case DSB:  do_rx_SBCW(); break;
532   case AM:
533   case SAM:  do_rx_AM(); break;
534   case FMN:  do_rx_FM();   break;
535   case DRM:  do_rx_DRM();  break;
536   case SPEC:
537     default: do_rx_SPEC(); break;
538   }
539   do_rx_post();
540 }  
541
542 //==============================================================
543 /* TX processing */
544
545 /* pre-condition for (nearly) all TX modes */
546
547 PRIVATE void
548 do_tx_pre(void) {
549
550   if (tx.scl.pre.flag) {
551     int i, n = CXBhave(tx.buf.i);
552     for (i = 0; i < n; i++)
553       CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0);
554   }
555
556   //
557   // mix in CW tone here?
558   //
559
560   correctIQ(tx.buf.i, tx.iqfix);
561
562   if (tx.spr.flag) SpeechProcessor(tx.spr.gen);
563
564   if (tx.tick == 0) reset_OvSv(tx.filt.ovsv);
565   filter_OvSv(tx.filt.ovsv);
566 }
567
568 PRIVATE void
569 do_tx_post(void) {
570   CXBhave(tx.buf.o) = CXBhave(tx.buf.i);
571
572   if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick);
573   if (tx.scl.post.flag) {
574     int i, n = CXBhave(tx.buf.o);
575     for (i = 0; i < n; i++)
576       CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val);
577   }
578   if (uni.meter.flag) do_meter(CXBbase(tx.buf.o), uni.buflen);
579   if (tx.osc.gen->Frequency != 0.0) {
580     int i;
581     ComplexOSC(tx.osc.gen);
582     for (i = 0; i < CXBhave(tx.buf.o); i++)
583       CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i));
584   }
585 }
586
587 /* modulator processing */
588
589 PRIVATE void
590 do_tx_SBCW(void) {
591   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
592
593   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
594     for (i = 0; i < n; i++) {
595       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99),
596                        Cscl(CXBdata(tx.buf.o, i), -0.01));
597       CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc);
598     }
599 }
600
601 PRIVATE void
602 do_tx_AM(void) {
603   int i, n = min(CXBhave(tx.buf.o), uni.buflen); 
604
605   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
606     for (i = 0; i < n; i++) { 
607       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
608                        Cscl(CXBdata(tx.buf.o, i), -0.001));
609       CXBreal(tx.buf.o, i) =
610         0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re);
611       CXBimag(tx.buf.o, i) = 0.0;
612     }
613 }
614
615 PRIVATE void
616 do_tx_FM(void) {
617   int i, n = min(CXBhave(tx.buf.o), uni.buflen);
618   if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0)
619     for (i = 0; i < n; i++) {
620       tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999),
621                        Cscl(CXBdata(tx.buf.o, i), 0.001));
622       tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq;
623       if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI;
624       if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI;
625       CXBdata(tx.buf.o, i) =
626         Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999);
627     }
628 }
629
630 PRIVATE void
631 do_tx_NIL(void) {
632   int i, n = min(CXBhave(tx.buf.i), uni.buflen);
633   for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero;
634 }
635
636 /* general TX processing dispatch */
637
638 PRIVATE void
639 do_tx(void) {
640   do_tx_pre();
641   switch (tx.mode) {
642   case USB:
643   case LSB:
644   case CWU:
645   case CWL:
646   case DSB:  do_tx_SBCW(); break;
647   case AM:
648   case SAM:  do_tx_AM();   break;
649   case FMN:  do_tx_FM();   break;
650   case DRM:
651   case SPEC:
652     default: do_tx_NIL(); break;
653   }
654   do_tx_post();
655 }
656
657 //========================================================================
658 /* overall buffer processing;
659    come here when there are buffers to work on */
660
661 void
662 process_samples(float *bufl, float *bufr, int n) {
663   int i;
664
665   switch (uni.mode.trx) {
666
667   case RX:
668     for (i = 0; i < n; i++)
669       CXBimag(rx.buf.i, i) = bufl[i], CXBreal(rx.buf.i, i) = bufr[i];
670     CXBhave(rx.buf.i) = n;
671
672     do_rx(), rx.tick++;
673
674     for (i = 0; i < n; i++)
675       bufl[i] = (float) CXBimag(rx.buf.o, i), bufr[i] = (float) CXBreal(rx.buf.o, i);
676     CXBhave(rx.buf.o) = n;
677     break;
678
679   case TX:
680     for (i = 0; i < n; i++)
681       CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i];
682     CXBhave(tx.buf.i) = n;
683
684     do_tx(), tx.tick++;
685
686     for (i = 0; i < n; i++)
687       bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i);
688     CXBhave(tx.buf.o) = n;
689     break;
690   }
691
692   uni.tick++;
693 }