35 #include <X11/Xutil.h> 37 static const int DEFAULT_GUI_DECIMATION = 64;
47 gfx(scheduler *sch,
const char *name)
50 for (wp = sch->windows; wp && wp->name; ++wp)
51 if (!strcmp(wp->name, name))
54 init(wp->name, wp->x, wp->y, wp->w, wp->h);
57 fprintf(stderr,
"No placement hints for window '%s'\n", name);
58 init(name, -1, -1, 320, 240);
61 gfx(
const char *name,
int _x,
int _y,
int _w,
int _h)
63 init(name, _x, _y, _w, _h);
65 void init(
const char *name,
int _x,
int _y,
int _w,
int _h)
72 display = XOpenDisplay(getenv(
"DISPLAY"));
75 screen = DefaultScreen(display);
76 XSetWindowAttributes xswa;
77 xswa.event_mask = (ExposureMask |
84 xswa.background_pixel = BlackPixel(display, screen);
85 window = XCreateWindow(display, DefaultRootWindow(display),
86 100, 100, w, h, 10, CopyFromParent, InputOutput,
87 CopyFromParent, CWEventMask | CWBackPixel,
91 XStoreName(display, window, name);
92 XMapWindow(display, window);
93 if (_x >= 0 && _y >= 0)
94 XMoveWindow(display, window, _x, _y);
95 dbuf = XCreatePixmap(display, window, w, h, DefaultDepth(display, screen));
96 gc = XCreateGC(display, dbuf, 0, NULL);
103 XFillRectangle(display, dbuf, gc, 0, 0, w, h);
107 XCopyArea(display, dbuf, window, gc, 0, 0, w, h, 0, 0);
111 XSync(display, False);
116 while (XCheckWindowEvent(display, window, -1, &ev))
122 int b = ev.xbutton.button;
131 int b = ev.xbutton.button;
132 buttons &= ~(1 << b);
145 void setfg(
unsigned char r,
unsigned char g,
unsigned char b)
151 c.flags = DoRed | DoGreen | DoBlue;
152 if (!XAllocColor(display, DefaultColormap(display, screen), &c))
154 XSetForeground(display, gc, c.pixel);
156 void point(
int x,
int y)
158 XDrawPoint(display, dbuf, gc, x, y);
160 void line(
int x0,
int y0,
int x1,
int y1)
162 XDrawLine(display, dbuf, gc, x0, y0, x1, y1);
164 void text(
int x,
int y,
const char *s)
166 XDrawString(display, dbuf, gc, x, y, s, strlen(s));
168 void transient_text(
int x,
int y,
const char *s)
170 XDrawString(display, window, gc, x, y, s, strlen(s));
178 template <
typename T>
179 struct cscope : runnable
183 unsigned long pixels_per_frame;
185 cscope(scheduler *sch, pipebuf<complex<T>> &_in, T _xymin, T _xymax,
186 const char *_name = NULL)
187 : runnable(sch, _name ? _name : _in.name),
188 xymin(_xymin), xymax(_xymax),
189 decimation(DEFAULT_GUI_DECIMATION), pixels_per_frame(1024),
191 in(_in), phase(0), g(sch, name)
196 while (in.readable() >= pixels_per_frame)
202 complex<T> *p = in.rd(), *pend = p + pixels_per_frame;
203 for (; p < pend; ++p)
204 g.point(g.w * (p->re - xymin) / (xymax - xymin),
205 g.h - g.h * (p->im - xymin) / (xymax - xymin));
206 if (cstln && (*cstln))
209 g.setfg(255, 255, 255);
210 for (
int i = 0;
i < (*cstln)->nsymbols; ++
i)
212 complex<signed char> *p = &(*cstln)->symbols[
i];
213 int x = g.w * (p->re - xymin) / (xymax - xymin);
214 int y = g.h - g.h * (p->im - xymin) / (xymax - xymin);
215 for (
int d = -2; d <= 2; ++d)
225 in.read(pixels_per_frame);
226 if (++phase >= decimation)
231 pipereader<complex<T>> in;
238 g.line(g.w / 2, 0, g.w / 2, g.h);
239 g.line(0, g.h / 2, g.w, g.h / 2);
243 template <
typename T>
244 struct wavescope : runnable
249 wavescope(scheduler *sch, pipebuf<T> &_in,
250 T _ymin, T _ymax,
const char *_name = NULL)
251 : runnable(sch, _name ? _name : _in.name),
252 ymin(_ymin), ymax(_ymax),
253 decimation(DEFAULT_GUI_DECIMATION), hgrid(0),
255 phase(0), g(sch, name), x(0)
261 while (in.readable() >= g.w)
266 if (++phase >= decimation)
270 void plot(T *p,
int count)
274 g.setfg(128, 128, 0);
275 g.line(0, g.h / 2, g.w - 1, g.h / 2);
278 for (
float x = 0; x < g.w; x += hgrid)
279 g.line(x, 0, x, g.h - 1);
282 for (
int x = 0; p < pend; ++x, ++p)
285 g.point(x, g.h - 1 - (g.h - 1) * (v - ymin) / (ymax - ymin));
298 template <
typename T>
299 struct slowmultiscope : runnable
304 const char *name, *format;
305 unsigned char rgb[3];
319 unsigned long samples_per_pixel;
321 slowmultiscope(scheduler *sch,
const chanspec *specs,
int nspecs,
323 : runnable(sch, _name ? _name :
"slowmultiscope"),
324 samples_per_pixel(1), sample_freq(1),
325 g(sch, name), t(0), x(0), total_samples(0)
327 chans =
new channel[nspecs];
329 for (
int i = 0;
i < nspecs; ++
i)
331 if (specs[
i].flags & chanspec::DISABLED)
333 chans[nchans].spec = specs[
i];
334 chans[nchans].in =
new pipereader<T>(*specs[
i].in);
335 chans[nchans].accum = 0;
344 for (channel *c = chans; c < chans + nchans; ++c)
346 if (c->spec.flags & chanspec::ASYNC)
347 run_channel(c, c->in->readable());
351 unsigned long count = samples_per_pixel;
352 for (channel *c = chans; c < chans + nchans; ++c)
353 if (!(c->spec.flags & chanspec::ASYNC))
354 count =
min(count, c->in->readable());
355 for (
int n = count; n--;)
357 for (channel *c = chans; c < chans + nchans; ++c)
358 if (!(c->spec.flags & chanspec::ASYNC))
364 for (
int i = 0;
i < nchans; ++
i)
366 channel *c = &chans[
i];
367 g.setfg(c->spec.rgb[0], c->spec.rgb[1], c->spec.rgb[2]);
369 sprintf(text, c->spec.format, c->print_val);
370 g.transient_text(5, 20 + 16 *
i, text);
373 if (t >= samples_per_pixel)
380 g.line(x, 0, x, g.h - 1);
384 total_samples += count;
397 void run_channel(channel *c,
int nr)
399 g.setfg(c->spec.rgb[0], c->spec.rgb[1], c->spec.rgb[2]);
403 float v = *c->in->rd() * c->spec.scale;
404 if (c->spec.flags & chanspec::COUNT)
406 else if (c->spec.flags & chanspec::SUM)
411 float nv = (v - c->spec.ymin) / (c->spec.ymax - c->spec.ymin);
412 if (c->spec.flags & chanspec::WRAP)
419 if ((c->spec.flags & (chanspec::COUNT | chanspec::SUM)) &&
420 t + 1 >= samples_per_pixel)
423 y = g.h - 1 - g.h * (v - c->spec.ymin) / (c->spec.ymax - c->spec.ymin);
429 if (c->spec.flags & chanspec::LINE)
432 g.line(x - 1, c->prev_y, x, y);
443 float ct = g.mx * samples_per_pixel / sample_freq;
444 float tt = total_samples / sample_freq;
446 sprintf(text,
"%.3f / %.3f s", ct, tt);
447 g.setfg(255, 255, 255);
448 g.transient_text(g.w * 3 / 4, 20, text);
456 template <
typename T>
457 struct spectrumscope : runnable
462 spectrumscope(scheduler *sch, pipebuf<complex<T>> &_in,
463 T _max,
const char *_name = NULL)
464 : runnable(sch, _name ? _name : _in.name),
465 size(4096), amax(_max / sqrtf(size)),
466 decimation(DEFAULT_GUI_DECIMATION),
469 phase(0), g(sch, name), fft(NULL)
472 void mark_freq(
float f)
474 if (nmarkers == MAX_MARKERS)
475 fail(
"Too many markers");
476 markers[nmarkers++] = f;
480 while (in.readable() >= size)
485 if (++phase >= decimation)
491 pipereader<complex<T>> in;
492 static const int MAX_MARKERS = 4;
493 float markers[MAX_MARKERS];
497 cfft_engine<float> *fft;
498 void do_fft(complex<T> *input)
501 if (!fft || fft->n != size)
505 fft =
new cfft_engine<float>(size);
507 complex<T> *pin = input, *pend = pin + size;
508 complex<float> data[size], *pout = data;
510 for (
int x = 0; pin < pend; ++pin, ++pout, ++x)
512 pout->re = (float)pin->re;
513 pout->im = (
float)pin->im;
516 fft->inplace(data,
true);
518 for (
int i = 0;
i < size; ++
i)
520 int x = ((
i < size / 2) ?
i + size / 2 :
i - size / 2) * g.w / size;
521 complex<float> v = data[
i];
523 float y = hypot(v.re, v.im);
524 g.line(x, g.h - 1, x, g.h - 1 - y * g.h / amax);
529 float f = 2.4e6 * (g.mx - g.w / 2) / g.w;
539 g.setfg(255, 255, 255);
540 g.line(g.w / 2, 0, g.w / 2, g.h);
542 for (
int i = 0;
i < nmarkers; ++
i)
544 int x = g.w * (0.5 + markers[
i]);
545 g.line(x, 0, x, g.h);
550 template <
typename T>
551 struct rfscope : runnable
562 rfscope(scheduler *sch, pipebuf<complex<T>> &_in,
563 const char *_name = NULL)
564 : runnable(sch, _name ? _name : _in.name),
565 size(4096), decimation(DEFAULT_GUI_DECIMATION),
566 Fs(1), Fc(0), ncursors(0), hzoom(1),
567 db0(-25), dbrange(50), bw(0.05),
568 in(_in), phase(0), g(sch, name), fft(NULL), filtered(NULL)
573 while (in.readable() >= size)
578 if (++phase >= decimation)
584 pipereader<complex<T>> in;
587 cfft_engine<float> *fft;
590 void do_fft(complex<T> *input)
594 if (!fft || fft->n != size)
598 fft =
new cfft_engine<float>(size);
601 complex<T> *pin = input, *pend = pin + size;
602 complex<float> data[size], *pout = data;
603 for (
int x = 0; pin < pend; ++pin, ++pout, ++x)
605 pout->re = (float)pin->re;
606 pout->im = (
float)pin->im;
608 fft->inplace(data,
true);
610 for (
int i = 0;
i < size; ++
i)
612 complex<float> &v = data[
i];
614 amp2[
i] = (v.re * v.re + v.im * v.im) * size;
618 filtered =
new float[size];
619 for (
int i = 0;
i < size; ++
i)
620 filtered[
i] = amp2[
i];
622 float bwcomp = 1 - bw;
624 for (
int i = 0;
i < size; ++
i)
626 filtered[
i] = amp2[
i] * bw + filtered[
i] * bwcomp;
627 float db = filtered[
i] ? 10 * logf(filtered[
i]) / logf(10) : db0;
628 int is = (
i < size / 2) ?
i :
i - size;
629 int x = g.w / 2 + is * hzoom * g.w / size;
630 int y = g.h - 1 - (db - db0) * g.h / dbrange;
631 g.line(x, g.h - 1, x, y);
636 float freq = Fc + Fs * (g.mx - g.w / 2) / g.w / hzoom;
637 float val = db0 + (
float)((g.h - 1) - g.my) * dbrange / g.h;
638 sprintf(s,
"%f.3 Hz %f.2 dB", freq, val);
639 g.setfg(255, 255, 255);
643 g.setfg(255, 255, 0);
644 for (
int i = 0;
i < ncursors; ++
i)
646 int x = g.w / 2 + (cursors[
i] - Fc) * hzoom * g.w / Fs;
647 g.line(x, 0, x, g.h - 1);
657 for (
float db = floorf(db0); db < db0 + dbrange; ++db)
659 int y = g.h - 1 - (db - db0) * g.h / dbrange;
660 g.line(0, y, g.w - 1, y);
663 g.setfg(255, 255, 255);
664 g.line(g.w / 2, 0, g.w / 2, g.h);
668 template <
typename T>
669 struct genscope : runnable
681 genscope(scheduler *sch, chanspec *specs,
int _nchans,
682 const char *_name = NULL)
683 : runnable(sch, _name ? _name :
"genscope"),
687 chans =
new channel[nchans];
688 for (
int i = 0;
i < nchans; ++
i)
696 chans[
i].spec = specs[
i];
697 chans[
i].in =
new pipereader<T>(*specs[
i].in);
701 gettimeofday(&tv, NULL);
713 for (channel *pc = chans; pc < chans + nchans; ++pc)
717 int n = pc->in->readable();
718 T last = pc->in->rd()[n - 1];
720 int dx = pc->spec.r.dir ==
'h' ? last : 0;
721 int dy = pc->spec.r.dir ==
'v' ? last : 0;
724 g.line(pc->spec.r.x - dx, pc->spec.r.y - dy,
725 pc->spec.r.x + dx, pc->spec.r.y + dy);
727 sprintf(txt,
"%d", (
int)last);
728 g.text(pc->spec.r.x + 5, pc->spec.r.y - 2, txt);
730 struct timeval newtv;
731 gettimeofday(&newtv, NULL);
732 int dt = (newtv.tv_sec - tv.tv_sec) * 1000 + (newtv.tv_usec - tv.tv_usec) / 1000;
735 fprintf(stderr,
"#");
751 #endif // LEANSDR_GUI_H int decimation(float Fin, float Fout)
Fixed< IntType, IntBits > floor(Fixed< IntType, IntBits > const &x)
void fatal(const char *s)
T min(const T &x, const T &y)