3 This file is part of a program that implements a Software-Defined Radio.
5 Copyright (C) 2004-5 by Frank Brickle, AB2KT and Bob McGwier, N4HY
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 The authors can be reached by email at
29 The DTTS Microwave Society
36 /////////////////////////////////////////////////////////////////////////
37 // elementary defaults
41 /////////////////////////////////////////////////////////////////////////
42 // most of what little we know here about the inner loop,
43 // functionally speaking
45 extern void reset_meters(void);
46 extern void reset_spectrum(void);
47 extern void reset_counters(void);
48 extern void process_samples(float *, float *, float *, float *, int);
49 extern void setup_workspace(void);
50 extern void destroy_workspace(void);
51 extern void clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes);
53 //========================================================================
56 spectrum_thread(void) {
59 sem_wait(&top.sync.pws.sem);
61 compute_spectrum(&uni.spec);
63 if (fwrite((char *) &uni.spec.label, sizeof(int), 1, top.meas.spec.fp)
65 fprintf(stderr, "error writing spectrum label\n");
69 if (fwrite((char *) uni.spec.output, sizeof(float), uni.spec.size, top.meas.spec.fp)
71 fprintf(stderr, "error writing spectrum\n");
75 if (fwrite((char *) uni.spec.oscope, sizeof(float), uni.spec.size, top.meas.spec.fp)
77 fprintf(stderr, "error writing oscope\n");
81 fflush(top.meas.spec.fp);
91 sem_wait(&top.sync.mtr.sem);
93 if (fwrite((char *) &uni.meter.label, sizeof(int), 1, top.meas.mtr.fp)
95 fprintf(stderr, "error writing meter label\n");
99 if (fwrite((char *) uni.meter.snap.rx, sizeof(REAL), MAXRX * RXMETERPTS, top.meas.mtr.fp)
100 != MAXRX * RXMETERPTS) {
101 fprintf(stderr, "error writing rx meter\n");
105 if (fwrite((char *) uni.meter.snap.tx, sizeof(REAL), TXMETERPTS, top.meas.mtr.fp)
107 fprintf(stderr, "error writing tx meter\n");
111 fflush(top.meas.mtr.fp);
117 //========================================================================
120 monitor_thread(void) {
121 while (top.running) {
122 sem_wait(&top.sync.mon.sem);
124 "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
130 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
135 //========================================================================
138 process_updates_thread(void) {
139 while (top.running) {
140 pthread_testcancel();
141 while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
142 do_update(top.parm.buff, top.verbose ? stderr : 0);
147 //========================================================================
151 if (jack_ringbuffer_write_space(top.jack.ring.o.l)
152 < top.hold.size.bytes) {
154 jack_ringbuffer_reset(top.jack.ring.o.l);
155 jack_ringbuffer_reset(top.jack.ring.o.r);
156 top.jack.blow.rb.o++;
158 jack_ringbuffer_write(top.jack.ring.o.l,
159 (char *) top.hold.buf.l,
160 top.hold.size.bytes);
161 jack_ringbuffer_write(top.jack.ring.o.r,
162 (char *) top.hold.buf.r,
163 top.hold.size.bytes);
164 if (jack_ringbuffer_read_space(top.jack.ring.i.l)
165 < top.hold.size.bytes) {
167 jack_ringbuffer_reset(top.jack.ring.i.l);
168 jack_ringbuffer_reset(top.jack.ring.i.r);
169 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
170 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
171 jack_ringbuffer_reset(top.jack.auxr.i.l);
172 jack_ringbuffer_reset(top.jack.auxr.i.r);
173 memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
174 memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
175 top.jack.blow.rb.i++;
177 jack_ringbuffer_read(top.jack.ring.i.l,
178 (char *) top.hold.buf.l,
179 top.hold.size.bytes);
180 jack_ringbuffer_read(top.jack.ring.i.r,
181 (char *) top.hold.buf.r,
182 top.hold.size.bytes);
183 jack_ringbuffer_read(top.jack.auxr.i.l,
184 (char *) top.hold.aux.l,
185 top.hold.size.bytes);
186 jack_ringbuffer_read(top.jack.auxr.i.r,
187 (char *) top.hold.aux.r,
188 top.hold.size.bytes);
195 jack_ringbuffer_read_space(top.jack.ring.i.l)
196 >= top.hold.size.bytes;
199 //------------------------------------------------------------------------
203 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
204 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
205 memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
206 memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
211 run_pass(void) { uni.tick++; }
215 process_samples(top.hold.buf.l, top.hold.buf.r,
216 top.hold.aux.l, top.hold.aux.r,
217 top.hold.size.frames);
220 // NB do not set RUN_SWCH directly via setRunState;
221 // use setSWCH instead
225 if (top.swch.bfct.have == 0) {
228 int i, m = top.swch.fade, n = top.swch.tail;
229 for (i = 0; i < m; i++) {
230 float w = (float) 1.0 - (float) i / m;
231 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
233 memset((char *) (top.hold.buf.l + m), 0, n);
234 memset((char *) (top.hold.buf.r + m), 0, n);
235 top.swch.bfct.have++;
236 } else if (top.swch.bfct.have < top.swch.bfct.want) {
238 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
239 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
240 top.swch.bfct.have++;
244 int i, m = top.swch.fade, n = top.swch.tail;
245 for (i = 0; i < m; i++) {
246 float w = (float) i / m;
247 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
249 uni.mode.trx = top.swch.trx.next;
251 top.state = top.swch.run.last;
252 top.swch.bfct.want = top.swch.bfct.have = 0;
254 jack_ringbuffer_reset(top.jack.ring.o.l);
255 jack_ringbuffer_reset(top.jack.ring.o.r);
256 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
257 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
264 process_samples(top.hold.buf.l, top.hold.buf.r,
265 top.hold.aux.l, top.hold.aux.r,
266 top.hold.size.frames);
269 //========================================================================
272 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
275 for (i = 0; i < nbytes; i++)
276 jack_ringbuffer_write(rb, &zero, 1);
280 audio_callback(jack_nframes_t nframes, void *arg) {
282 int nbytes = nframes * sizeof(float);
284 if (nframes == top.jack.size) {
286 // output: copy from ring to port
287 lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
288 rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
290 if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
291 jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
292 jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
293 } else { // rb pathology
294 memset((char *) lp, 0, nbytes);
295 memset((char *) rp, 0, nbytes);
296 jack_ringbuffer_reset(top.jack.ring.o.l);
297 jack_ringbuffer_reset(top.jack.ring.o.r);
298 clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
299 clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
300 top.jack.blow.rb.o++;
303 // input: copy from port to ring
304 if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
305 lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
306 rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
307 jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
308 jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
309 lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
310 rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
311 jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
312 jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
313 } else { // rb pathology
314 jack_ringbuffer_reset(top.jack.ring.i.l);
315 jack_ringbuffer_reset(top.jack.ring.i.r);
316 clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
317 clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
318 jack_ringbuffer_reset(top.jack.auxr.i.l);
319 jack_ringbuffer_reset(top.jack.auxr.i.r);
320 clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
321 clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
322 top.jack.blow.rb.i++;
325 } else { // callback pathology
326 jack_ringbuffer_reset(top.jack.ring.i.l);
327 jack_ringbuffer_reset(top.jack.ring.i.r);
328 jack_ringbuffer_reset(top.jack.ring.o.l);
329 jack_ringbuffer_reset(top.jack.ring.o.r);
330 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
331 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
335 // if enough accumulated in ring, fire dsp
336 if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
337 sem_post(&top.sync.buf.sem);
340 if ((top.jack.blow.cb > 0) ||
341 (top.jack.blow.rb.i > 0) ||
342 (top.jack.blow.rb.o > 0))
343 sem_post(&top.sync.mon.sem);
346 //========================================================================
349 process_samples_thread(void) {
350 while (top.running) {
351 sem_wait(&top.sync.buf.sem);
354 sem_wait(&top.sync.upd.sem);
356 case RUN_MUTE: run_mute(); break;
357 case RUN_PASS: run_pass(); break;
358 case RUN_PLAY: run_play(); break;
359 case RUN_SWCH: run_swch(); break;
361 sem_post(&top.sync.upd.sem);
367 //========================================================================
372 sem_post(&top.sync.upd.sem);
376 FILE *frc = find_rcfile(loc.path.rcfile);
378 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
379 do_update(top.parm.buff, top.verbose ? stderr : 0);
384 // start audio processing
385 if (jack_activate(top.jack.client))
386 perror("cannot activate jack client"), exit(1);
388 // wait for threads to terminate
389 pthread_join(top.thrd.trx.id, 0);
390 pthread_join(top.thrd.upd.id, 0);
391 pthread_join(top.thrd.mon.id, 0);
393 pthread_join(top.thrd.mtr.id, 0);
395 pthread_join(top.thrd.pws.id, 0);
397 // stop audio processing
398 jack_client_close(top.jack.client);
403 jack_ringbuffer_free(top.jack.auxr.i.r);
404 jack_ringbuffer_free(top.jack.auxr.i.l);
405 jack_ringbuffer_free(top.jack.ring.o.r);
406 jack_ringbuffer_free(top.jack.ring.o.l);
407 jack_ringbuffer_free(top.jack.ring.i.r);
408 jack_ringbuffer_free(top.jack.ring.i.l);
410 safefree((char *) top.hold.buf.r);
411 safefree((char *) top.hold.buf.l);
412 safefree((char *) top.hold.aux.r);
413 safefree((char *) top.hold.aux.l);
418 fclose(top.meas.mtr.fp);
420 fclose(top.meas.spec.fp);
429 fprintf(stderr, "usage:\n");
430 fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
431 fprintf(stderr, "flags:\n");
432 fprintf(stderr, " -v verbose commentary\n");
433 fprintf(stderr, " -m do metering\n");
434 fprintf(stderr, " -s do spectrum\n");
435 fprintf(stderr, " -l file execute update commands in file at startup\n");
436 fprintf(stderr, "'file' arg unused, but available\n");
443 arg = fcntl(fd, F_GETFL);
445 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
446 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
447 fcntl(fd, F_SETFL, arg);
450 //........................................................................
453 setup_switching(void) {
454 top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
455 top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
459 setup_local_audio(void) {
460 top.hold.size.frames = uni.buflen;
461 top.hold.size.bytes = top.hold.size.frames * sizeof(float);
462 top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
463 "main hold buffer left");
464 top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
465 "main hold buffer right");
466 top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
467 "aux hold buffer left");
468 top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
469 "aux hold buffer right");
473 setup_updates(void) {
474 top.parm.path = loc.path.parm;
475 if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
476 perror(top.parm.path), exit(1);
477 if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
478 fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
482 // do this here 'cuz the update thread is controlling the action
483 if (uni.meter.flag) {
484 top.meas.mtr.path = loc.path.meter;
485 top.meas.mtr.fp = efopen(top.meas.mtr.path, "r+");
488 top.meas.spec.path = loc.path.spec;
489 top.meas.spec.fp = efopen(top.meas.spec.path, "r+");
494 jack_xrun(void *arg) {
496 sem_post(&top.sync.mon.sem);
500 jack_shutdown(void *arg) {}
503 setup_system_audio(void) {
504 if (loc.name[0]) strcpy(top.jack.name, loc.name);
505 else sprintf(top.jack.name, "sdr-%d", top.pid);
506 if (!(top.jack.client = jack_client_new(top.jack.name)))
507 perror("can't make client -- jack not running?"), exit(1);
509 jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
510 jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
511 jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
512 top.jack.size = jack_get_buffer_size(top.jack.client);
513 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
515 top.jack.port.i.l = jack_port_register(top.jack.client,
517 JACK_DEFAULT_AUDIO_TYPE,
520 top.jack.port.i.r = jack_port_register(top.jack.client,
522 JACK_DEFAULT_AUDIO_TYPE,
525 top.jack.port.o.l = jack_port_register(top.jack.client,
527 JACK_DEFAULT_AUDIO_TYPE,
530 top.jack.port.o.r = jack_port_register(top.jack.client,
532 JACK_DEFAULT_AUDIO_TYPE,
535 top.jack.auxp.i.l = jack_port_register(top.jack.client,
537 JACK_DEFAULT_AUDIO_TYPE,
540 top.jack.auxp.i.r = jack_port_register(top.jack.client,
542 JACK_DEFAULT_AUDIO_TYPE,
545 top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
546 top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
547 top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
548 top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
549 top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
550 top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
551 clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
552 clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
556 setup_threading(void) {
557 sem_init(&top.sync.upd.sem, 0, 0);
558 pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
559 sem_init(&top.sync.buf.sem, 0, 0);
560 pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
561 sem_init(&top.sync.mon.sem, 0, 0);
562 pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
563 if (uni.meter.flag) {
564 sem_init(&top.sync.mtr.sem, 0, 0);
565 pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL);
568 sem_init(&top.sync.pws.sem, 0, 0);
569 pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL);
573 //========================================================================
574 // hard defaults, then environment
577 setup_defaults(void) {
578 loc.name[0] = 0; // no default name for jack client
579 strcpy(loc.path.rcfile, RCBASE);
580 strcpy(loc.path.parm, PARMPATH);
581 strcpy(loc.path.meter, METERPATH);
582 strcpy(loc.path.spec, SPECPATH);
583 strcpy(loc.path.wisdom, WISDOMPATH);
584 loc.def.rate = DEFRATE;
585 loc.def.size = DEFSIZE;
586 loc.def.mode = DEFMODE;
587 loc.def.spec = DEFSPEC;
588 loc.def.comp = DEFCOMP;
590 loc.mult.ring = RINGMULT;
594 if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
595 if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
596 if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
597 if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
598 if ((ep = getenv("SDR_SPECPATH"))) strcpy(loc.path.spec, ep);
599 if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
600 if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
601 if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
602 if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
603 if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
607 //========================================================================
609 setup(int argc, char **argv) {
614 top.start_tv = now_tv();
617 top.state = RUN_PLAY;
621 for (i = 1; i < argc; i++)
622 if (argv[i][0] == '-')
623 switch (argv[i][1]) {
628 uni.meter.flag = TRUE;
631 uni.spec.flag = TRUE;
634 strcpy(loc.path.rcfile, argv[++i]);
641 if (!freopen(argv[i], "r", stdin))
642 perror(argv[i]), exit(1);
650 setup_system_audio();
656 //========================================================================
659 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }