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;
250 uni.mode.trx = top.swch.trx.next;
251 switch (uni.mode.trx) {
254 tx.agc.gen->over = tx.tick + 3;
257 for(i = 0; i < uni.multirx.nrx; i++)
258 rx[i].agc.gen->over = rx[i].tick + 3;
262 top.state = top.swch.run.last;
263 top.swch.bfct.want = top.swch.bfct.have = 0;
265 jack_ringbuffer_reset(top.jack.ring.o.l);
266 jack_ringbuffer_reset(top.jack.ring.o.r);
267 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
268 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
275 process_samples(top.hold.buf.l, top.hold.buf.r,
276 top.hold.aux.l, top.hold.aux.r,
277 top.hold.size.frames);
280 //========================================================================
283 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
286 for (i = 0; i < nbytes; i++)
287 jack_ringbuffer_write(rb, &zero, 1);
291 audio_callback(jack_nframes_t nframes, void *arg) {
293 int nbytes = nframes * sizeof(float);
295 if (nframes == top.jack.size) {
297 // output: copy from ring to port
298 lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
299 rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
301 if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
302 jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
303 jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
304 } else { // rb pathology
305 memset((char *) lp, 0, nbytes);
306 memset((char *) rp, 0, nbytes);
307 jack_ringbuffer_reset(top.jack.ring.o.l);
308 jack_ringbuffer_reset(top.jack.ring.o.r);
309 clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
310 clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
311 top.jack.blow.rb.o++;
314 // input: copy from port to ring
315 if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
316 lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
317 rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
318 jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
319 jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
320 lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
321 rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
322 jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
323 jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
324 } else { // rb pathology
325 jack_ringbuffer_reset(top.jack.ring.i.l);
326 jack_ringbuffer_reset(top.jack.ring.i.r);
327 clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
328 clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
329 jack_ringbuffer_reset(top.jack.auxr.i.l);
330 jack_ringbuffer_reset(top.jack.auxr.i.r);
331 clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
332 clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
333 top.jack.blow.rb.i++;
336 } else { // callback pathology
337 jack_ringbuffer_reset(top.jack.ring.i.l);
338 jack_ringbuffer_reset(top.jack.ring.i.r);
339 jack_ringbuffer_reset(top.jack.ring.o.l);
340 jack_ringbuffer_reset(top.jack.ring.o.r);
341 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
342 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
346 // if enough accumulated in ring, fire dsp
347 if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
348 sem_post(&top.sync.buf.sem);
351 if ((top.jack.blow.cb > 0) ||
352 (top.jack.blow.rb.i > 0) ||
353 (top.jack.blow.rb.o > 0))
354 sem_post(&top.sync.mon.sem);
357 //========================================================================
360 process_samples_thread(void) {
361 while (top.running) {
362 sem_wait(&top.sync.buf.sem);
365 sem_wait(&top.sync.upd.sem);
367 case RUN_MUTE: run_mute(); break;
368 case RUN_PASS: run_pass(); break;
369 case RUN_PLAY: run_play(); break;
370 case RUN_SWCH: run_swch(); break;
372 sem_post(&top.sync.upd.sem);
378 //========================================================================
383 sem_post(&top.sync.upd.sem);
387 FILE *frc = find_rcfile(loc.path.rcfile);
389 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
390 do_update(top.parm.buff, top.verbose ? stderr : 0);
395 // start audio processing
396 if (jack_activate(top.jack.client))
397 perror("cannot activate jack client"), exit(1);
399 // wait for threads to terminate
400 pthread_join(top.thrd.trx.id, 0);
401 pthread_join(top.thrd.upd.id, 0);
402 pthread_join(top.thrd.mon.id, 0);
404 pthread_join(top.thrd.mtr.id, 0);
406 pthread_join(top.thrd.pws.id, 0);
408 // stop audio processing
409 jack_client_close(top.jack.client);
414 jack_ringbuffer_free(top.jack.auxr.i.r);
415 jack_ringbuffer_free(top.jack.auxr.i.l);
416 jack_ringbuffer_free(top.jack.ring.o.r);
417 jack_ringbuffer_free(top.jack.ring.o.l);
418 jack_ringbuffer_free(top.jack.ring.i.r);
419 jack_ringbuffer_free(top.jack.ring.i.l);
421 safefree((char *) top.hold.buf.r);
422 safefree((char *) top.hold.buf.l);
423 safefree((char *) top.hold.aux.r);
424 safefree((char *) top.hold.aux.l);
429 fclose(top.meas.mtr.fp);
431 fclose(top.meas.spec.fp);
440 fprintf(stderr, "usage:\n");
441 fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
442 fprintf(stderr, "flags:\n");
443 fprintf(stderr, " -v verbose commentary\n");
444 fprintf(stderr, " -m do metering\n");
445 fprintf(stderr, " -s do spectrum\n");
446 fprintf(stderr, " -l file execute update commands in file at startup\n");
447 fprintf(stderr, "'file' arg unused, but available\n");
454 arg = fcntl(fd, F_GETFL);
456 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
457 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
458 fcntl(fd, F_SETFL, arg);
461 //........................................................................
464 setup_switching(void) {
465 top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
466 top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
470 setup_local_audio(void) {
471 top.hold.size.frames = uni.buflen;
472 top.hold.size.bytes = top.hold.size.frames * sizeof(float);
473 top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
474 "main hold buffer left");
475 top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
476 "main hold buffer right");
477 top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
478 "aux hold buffer left");
479 top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
480 "aux hold buffer right");
484 setup_updates(void) {
485 top.parm.path = loc.path.parm;
486 if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
487 perror(top.parm.path), exit(1);
488 if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
489 fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
493 // do this here 'cuz the update thread is controlling the action
494 if (uni.meter.flag) {
495 top.meas.mtr.path = loc.path.meter;
496 top.meas.mtr.fp = efopen(top.meas.mtr.path, "r+");
499 top.meas.spec.path = loc.path.spec;
500 top.meas.spec.fp = efopen(top.meas.spec.path, "r+");
505 jack_xrun(void *arg) {
507 sem_post(&top.sync.mon.sem);
511 jack_shutdown(void *arg) {}
514 setup_system_audio(void) {
515 if (loc.name[0]) strcpy(top.jack.name, loc.name);
516 else sprintf(top.jack.name, "sdr-%d", top.pid);
517 if (!(top.jack.client = jack_client_new(top.jack.name)))
518 perror("can't make client -- jack not running?"), exit(1);
520 jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
521 jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
522 jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
523 top.jack.size = jack_get_buffer_size(top.jack.client);
524 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
526 top.jack.port.i.l = jack_port_register(top.jack.client,
528 JACK_DEFAULT_AUDIO_TYPE,
531 top.jack.port.i.r = jack_port_register(top.jack.client,
533 JACK_DEFAULT_AUDIO_TYPE,
536 top.jack.port.o.l = jack_port_register(top.jack.client,
538 JACK_DEFAULT_AUDIO_TYPE,
541 top.jack.port.o.r = jack_port_register(top.jack.client,
543 JACK_DEFAULT_AUDIO_TYPE,
546 top.jack.auxp.i.l = jack_port_register(top.jack.client,
548 JACK_DEFAULT_AUDIO_TYPE,
551 top.jack.auxp.i.r = jack_port_register(top.jack.client,
553 JACK_DEFAULT_AUDIO_TYPE,
556 top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
557 top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
558 top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
559 top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
560 top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
561 top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
562 clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
563 clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
567 setup_threading(void) {
568 sem_init(&top.sync.upd.sem, 0, 0);
569 pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
570 sem_init(&top.sync.buf.sem, 0, 0);
571 pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
572 sem_init(&top.sync.mon.sem, 0, 0);
573 pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
574 if (uni.meter.flag) {
575 sem_init(&top.sync.mtr.sem, 0, 0);
576 pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL);
579 sem_init(&top.sync.pws.sem, 0, 0);
580 pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL);
584 //========================================================================
585 // hard defaults, then environment
588 setup_defaults(void) {
589 loc.name[0] = 0; // no default name for jack client
590 strcpy(loc.path.rcfile, RCBASE);
591 strcpy(loc.path.parm, PARMPATH);
592 strcpy(loc.path.meter, METERPATH);
593 strcpy(loc.path.spec, SPECPATH);
594 strcpy(loc.path.wisdom, WISDOMPATH);
595 loc.def.rate = DEFRATE;
596 loc.def.size = DEFSIZE;
597 loc.def.mode = DEFMODE;
598 loc.def.spec = DEFSPEC;
599 loc.def.comp = DEFCOMP;
601 loc.mult.ring = RINGMULT;
605 if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
606 if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
607 if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
608 if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
609 if ((ep = getenv("SDR_SPECPATH"))) strcpy(loc.path.spec, ep);
610 if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
611 if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
612 if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
613 if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
614 if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
618 //========================================================================
620 setup(int argc, char **argv) {
625 top.start_tv = now_tv();
628 top.state = RUN_PLAY;
632 for (i = 1; i < argc; i++)
633 if (argv[i][0] == '-')
634 switch (argv[i][1]) {
639 uni.meter.flag = TRUE;
642 uni.spec.flag = TRUE;
645 strcpy(loc.path.rcfile, argv[++i]);
652 if (!freopen(argv[i], "r", stdin))
653 perror(argv[i]), exit(1);
661 setup_system_audio();
667 //========================================================================
670 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }