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);
52 //========================================================================
55 spectrum_thread(void) {
58 sem_wait(&top.sync.pws.sem);
60 compute_spectrum(&uni.spec);
62 if (fwrite((char *) &uni.spec.label, sizeof(int), 1, top.meas.spec.fp)
64 fprintf(stderr, "error writing spectrum label\n");
68 if (fwrite((char *) uni.spec.output, sizeof(float), uni.spec.size, top.meas.spec.fp)
70 fprintf(stderr, "error writing spectrum\n");
74 fflush(top.meas.spec.fp);
84 sem_wait(&top.sync.mtr.sem);
86 if (fwrite((char *) &uni.meter.label, sizeof(int), 1, top.meas.mtr.fp)
88 fprintf(stderr, "error writing meter label\n");
92 if (fwrite((char *) uni.meter.snap.rx, sizeof(REAL), MAXRX * RXMETERPTS, top.meas.mtr.fp)
93 != MAXRX * RXMETERPTS) {
94 fprintf(stderr, "error writing rx meter\n");
98 if (fwrite((char *) uni.meter.snap.tx, sizeof(REAL), TXMETERPTS, top.meas.mtr.fp)
100 fprintf(stderr, "error writing tx meter\n");
104 fflush(top.meas.mtr.fp);
110 //========================================================================
113 monitor_thread(void) {
114 while (top.running) {
115 sem_wait(&top.sync.mon.sem);
117 "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
123 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
128 //========================================================================
131 process_updates_thread(void) {
132 while (top.running) {
133 pthread_testcancel();
134 while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
135 do_update(top.parm.buff, top.verbose ? stderr : 0);
140 //========================================================================
144 if (jack_ringbuffer_write_space(top.jack.ring.o.l)
145 < top.hold.size.bytes) {
147 jack_ringbuffer_reset(top.jack.ring.o.l);
148 jack_ringbuffer_reset(top.jack.ring.o.r);
149 top.jack.blow.rb.o++;
151 jack_ringbuffer_write(top.jack.ring.o.l,
152 (char *) top.hold.buf.l,
153 top.hold.size.bytes);
154 jack_ringbuffer_write(top.jack.ring.o.r,
155 (char *) top.hold.buf.r,
156 top.hold.size.bytes);
157 if (jack_ringbuffer_read_space(top.jack.ring.i.l)
158 < top.hold.size.bytes) {
160 jack_ringbuffer_reset(top.jack.ring.i.l);
161 jack_ringbuffer_reset(top.jack.ring.i.r);
162 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
163 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
164 jack_ringbuffer_reset(top.jack.auxr.i.l);
165 jack_ringbuffer_reset(top.jack.auxr.i.r);
166 memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
167 memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
168 top.jack.blow.rb.i++;
170 jack_ringbuffer_read(top.jack.ring.i.l,
171 (char *) top.hold.buf.l,
172 top.hold.size.bytes);
173 jack_ringbuffer_read(top.jack.ring.i.r,
174 (char *) top.hold.buf.r,
175 top.hold.size.bytes);
176 jack_ringbuffer_read(top.jack.auxr.i.l,
177 (char *) top.hold.aux.l,
178 top.hold.size.bytes);
179 jack_ringbuffer_read(top.jack.auxr.i.r,
180 (char *) top.hold.aux.r,
181 top.hold.size.bytes);
188 jack_ringbuffer_read_space(top.jack.ring.i.l)
189 >= top.hold.size.bytes;
192 //------------------------------------------------------------------------
196 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
197 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
198 memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
199 memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
204 run_pass(void) { uni.tick++; }
208 process_samples(top.hold.buf.l, top.hold.buf.r,
209 top.hold.aux.l, top.hold.aux.r,
210 top.hold.size.frames);
213 // NB do not set RUN_SWCH directly via setRunState;
214 // use setSWCH instead
218 if (top.swch.bfct.have == 0) {
221 int i, m = top.swch.fade, n = top.swch.tail;
222 for (i = 0; i < m; i++) {
223 float w = (float) 1.0 - (float) i / m;
224 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
226 memset((char *) (top.hold.buf.l + m), 0, n);
227 memset((char *) (top.hold.buf.r + m), 0, n);
228 top.swch.bfct.have++;
229 } else if (top.swch.bfct.have < top.swch.bfct.want) {
231 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
232 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
233 top.swch.bfct.have++;
237 int i, m = top.swch.fade, n = top.swch.tail;
238 for (i = 0; i < m; i++) {
239 float w = (float) i / m;
240 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
242 uni.mode.trx = top.swch.trx.next;
244 top.state = top.swch.run.last;
245 top.swch.bfct.want = top.swch.bfct.have = 0;
247 jack_ringbuffer_reset(top.jack.ring.o.l);
248 jack_ringbuffer_reset(top.jack.ring.o.r);
255 process_samples(top.hold.buf.l, top.hold.buf.r,
256 top.hold.aux.l, top.hold.aux.r,
257 top.hold.size.frames);
260 //========================================================================
263 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
266 for (i = 0; i < nbytes; i++)
267 jack_ringbuffer_write(rb, &zero, 1);
271 audio_callback(jack_nframes_t nframes, void *arg) {
273 int nbytes = nframes * sizeof(float);
275 if (nframes == top.jack.size) {
277 // output: copy from ring to port
278 lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
279 rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
281 if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
282 jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
283 jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
284 } else { // rb pathology
285 memset((char *) lp, 0, nbytes);
286 memset((char *) rp, 0, nbytes);
287 jack_ringbuffer_reset(top.jack.ring.o.l);
288 jack_ringbuffer_reset(top.jack.ring.o.r);
289 clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
290 clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
291 top.jack.blow.rb.o++;
294 // input: copy from port to ring
295 if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
296 lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
297 rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
298 jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
299 jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
300 lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
301 rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
302 jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
303 jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
304 } else { // rb pathology
305 jack_ringbuffer_reset(top.jack.ring.i.l);
306 jack_ringbuffer_reset(top.jack.ring.i.r);
307 clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
308 clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
309 jack_ringbuffer_reset(top.jack.auxr.i.l);
310 jack_ringbuffer_reset(top.jack.auxr.i.r);
311 clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
312 clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
313 top.jack.blow.rb.i++;
316 } else { // callback pathology
317 jack_ringbuffer_reset(top.jack.ring.i.l);
318 jack_ringbuffer_reset(top.jack.ring.i.r);
319 jack_ringbuffer_reset(top.jack.ring.o.l);
320 jack_ringbuffer_reset(top.jack.ring.o.r);
321 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
322 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
326 // if enough accumulated in ring, fire dsp
327 if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
328 sem_post(&top.sync.buf.sem);
331 if ((top.jack.blow.cb > 0) ||
332 (top.jack.blow.rb.i > 0) ||
333 (top.jack.blow.rb.o > 0))
334 sem_post(&top.sync.mon.sem);
337 //========================================================================
340 process_samples_thread(void) {
341 while (top.running) {
342 sem_wait(&top.sync.buf.sem);
345 sem_wait(&top.sync.upd.sem);
347 case RUN_MUTE: run_mute(); break;
348 case RUN_PASS: run_pass(); break;
349 case RUN_PLAY: run_play(); break;
350 case RUN_SWCH: run_swch(); break;
352 sem_post(&top.sync.upd.sem);
358 //========================================================================
363 sem_post(&top.sync.upd.sem);
367 FILE *frc = find_rcfile(loc.path.rcfile);
369 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
370 do_update(top.parm.buff, top.verbose ? stderr : 0);
375 // start audio processing
376 if (jack_activate(top.jack.client))
377 perror("cannot activate jack client"), exit(1);
379 // wait for threads to terminate
380 pthread_join(top.thrd.trx.id, 0);
381 pthread_join(top.thrd.upd.id, 0);
382 pthread_join(top.thrd.mon.id, 0);
384 pthread_join(top.thrd.mtr.id, 0);
386 pthread_join(top.thrd.pws.id, 0);
388 // stop audio processing
389 jack_client_close(top.jack.client);
394 jack_ringbuffer_free(top.jack.auxr.i.r);
395 jack_ringbuffer_free(top.jack.auxr.i.l);
396 jack_ringbuffer_free(top.jack.ring.o.r);
397 jack_ringbuffer_free(top.jack.ring.o.l);
398 jack_ringbuffer_free(top.jack.ring.i.r);
399 jack_ringbuffer_free(top.jack.ring.i.l);
401 safefree((char *) top.hold.buf.r);
402 safefree((char *) top.hold.buf.l);
403 safefree((char *) top.hold.aux.r);
404 safefree((char *) top.hold.aux.l);
409 fclose(top.meas.mtr.fp);
411 fclose(top.meas.spec.fp);
420 fprintf(stderr, "usage:\n");
421 fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
422 fprintf(stderr, "flags:\n");
423 fprintf(stderr, " -v verbose commentary\n");
424 fprintf(stderr, " -m do metering\n");
425 fprintf(stderr, " -s do spectrum\n");
426 fprintf(stderr, " -l file execute update commands in file at startup\n");
427 fprintf(stderr, "'file' arg unused, but available\n");
434 arg = fcntl(fd, F_GETFL);
436 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
437 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
438 fcntl(fd, F_SETFL, arg);
441 //........................................................................
444 setup_switching(void) {
445 top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
446 top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
450 setup_local_audio(void) {
451 top.hold.size.frames = uni.buflen;
452 top.hold.size.bytes = top.hold.size.frames * sizeof(float);
453 top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
454 "main hold buffer left");
455 top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
456 "main hold buffer right");
457 top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
458 "aux hold buffer left");
459 top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
460 "aux hold buffer right");
464 setup_updates(void) {
465 top.parm.path = loc.path.parm;
466 if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
467 perror(top.parm.path), exit(1);
468 if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
469 fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
473 // do this here 'cuz the update thread is controlling the action
474 if (uni.meter.flag) {
475 top.meas.mtr.path = loc.path.meter;
476 top.meas.mtr.fp = efopen(top.meas.mtr.path, "r+");
479 top.meas.spec.path = loc.path.spec;
480 top.meas.spec.fp = efopen(top.meas.spec.path, "r+");
485 jack_xrun(void *arg) {
487 sem_post(&top.sync.mon.sem);
491 jack_shutdown(void *arg) {}
494 setup_system_audio(void) {
495 if (loc.name[0]) strcpy(top.jack.name, loc.name);
496 else sprintf(top.jack.name, "sdr-%d", top.pid);
497 if (!(top.jack.client = jack_client_new(top.jack.name)))
498 perror("can't make client -- jack not running?"), exit(1);
500 jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
501 jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
502 jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
503 top.jack.size = jack_get_buffer_size(top.jack.client);
504 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
506 top.jack.port.i.l = jack_port_register(top.jack.client,
508 JACK_DEFAULT_AUDIO_TYPE,
511 top.jack.port.i.r = jack_port_register(top.jack.client,
513 JACK_DEFAULT_AUDIO_TYPE,
516 top.jack.port.o.l = jack_port_register(top.jack.client,
518 JACK_DEFAULT_AUDIO_TYPE,
521 top.jack.port.o.r = jack_port_register(top.jack.client,
523 JACK_DEFAULT_AUDIO_TYPE,
526 top.jack.auxp.i.l = jack_port_register(top.jack.client,
528 JACK_DEFAULT_AUDIO_TYPE,
531 top.jack.auxp.i.r = jack_port_register(top.jack.client,
533 JACK_DEFAULT_AUDIO_TYPE,
536 top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
537 top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
538 top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
539 top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
540 top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
541 top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
542 clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
543 clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
547 setup_threading(void) {
548 sem_init(&top.sync.upd.sem, 0, 0);
549 pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
550 sem_init(&top.sync.buf.sem, 0, 0);
551 pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
552 sem_init(&top.sync.mon.sem, 0, 0);
553 pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
554 if (uni.meter.flag) {
555 sem_init(&top.sync.mtr.sem, 0, 0);
556 pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL);
559 sem_init(&top.sync.pws.sem, 0, 0);
560 pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL);
564 //========================================================================
565 // hard defaults, then environment
568 setup_defaults(void) {
569 loc.name[0] = 0; // no default name for jack client
570 strcpy(loc.path.rcfile, RCBASE);
571 strcpy(loc.path.parm, PARMPATH);
572 strcpy(loc.path.meter, METERPATH);
573 strcpy(loc.path.spec, SPECPATH);
574 strcpy(loc.path.wisdom, WISDOMPATH);
575 loc.def.rate = DEFRATE;
576 loc.def.size = DEFSIZE;
577 loc.def.mode = DEFMODE;
578 loc.def.spec = DEFSPEC;
580 loc.mult.ring = RINGMULT;
584 if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
585 if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
586 if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
587 if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
588 if ((ep = getenv("SDR_SPECPATH"))) strcpy(loc.path.spec, ep);
589 if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
590 if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
591 if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
592 if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
593 if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
597 //========================================================================
599 setup(int argc, char **argv) {
604 top.start_tv = now_tv();
607 top.state = RUN_PLAY;
611 for (i = 1; i < argc; i++)
612 if (argv[i][0] == '-')
613 switch (argv[i][1]) {
618 uni.meter.flag = TRUE;
621 uni.spec.flag = TRUE;
624 strcpy(loc.path.rcfile, argv[++i]);
631 if (!freopen(argv[i], "r", stdin))
632 perror(argv[i]), exit(1);
640 setup_system_audio();
646 //========================================================================
649 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }