/* * Custom Widgets * Copyright (C) 1999 Thomas Sailer <sailer@ife.ee.ethz.ch> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <math.h> #include "widgets.h" #include <gtk/gtkgc.h> #include <gtk/gtkmain.h> #define SCOPEMAXPOINTS 512 #define PRIO G_PRIORITY_LOW struct _ScopePoint { guint16 dx; guint16 y; }; static void scope_class_init(ScopeClass *klass); static void scope_init(Scope *trace); static void scope_finalize(GtkObject *object); static gint scope_expose(GtkWidget *widget, GdkEventExpose *event); static void scope_size_request(GtkWidget *widget, GtkRequisition *requisition); static gint scope_idle_callback(gpointer data); static GtkWidgetClass *scope_parent_class = NULL; static ScopeClass *scope_class = NULL; guint scope_get_type(void) { static guint trace_type = 0; if (!trace_type) { static const GtkTypeInfo trace_info = { "Scope", sizeof(Scope), sizeof(ScopeClass), (GtkClassInitFunc) scope_class_init, (GtkObjectInitFunc) scope_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; trace_type = gtk_type_unique(gtk_widget_get_type(), &trace_info); } return trace_type; } static void scope_class_init(ScopeClass *klass) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*)klass; widget_class = (GtkWidgetClass*)klass; scope_parent_class = gtk_type_class(gtk_widget_get_type()); scope_class = klass; object_class->finalize = scope_finalize; widget_class->size_request = scope_size_request; widget_class->expose_event = scope_expose; } static void scope_init(Scope *trace) { GTK_WIDGET_SET_FLAGS(trace, GTK_NO_WINDOW); trace->idlefunc = 0; /* initialize the colors */ trace->zerolinecol.red = 65535; trace->zerolinecol.green = 13107; trace->zerolinecol.blue = 0; trace->samplingcol.red = 13763; trace->samplingcol.green = 42598; trace->samplingcol.blue = 5243; trace->zeroline_gc = trace->sampling_gc = NULL; /* initialize the trace history */ trace->ptnum = trace->ptptr = 0; trace->pt = NULL; } static void scope_size_request(GtkWidget *widget, GtkRequisition *requisition) { Scope *trace; g_return_if_fail(widget != NULL); g_return_if_fail(IS_SCOPE(widget)); g_return_if_fail(requisition != NULL); trace = SCOPE(widget); requisition->width = MIN(trace->ptnum * 4, 512); requisition->height = 256; } GtkWidget* scope_new(char *name, char *str1, char *str2, gint int1, gint int2) { Scope *trace; trace = gtk_type_new(scope_get_type()); trace->ptnum = CLAMP(int1, 2, SCOPEMAXPOINTS);; trace->pt = g_malloc0(trace->ptnum * sizeof(struct _ScopePoint)); return GTK_WIDGET(trace); } static void scope_finalize(GtkObject *object) { Scope *trace; g_return_if_fail(object != NULL); g_return_if_fail(IS_SCOPE(object)); trace = SCOPE(object); if (trace->idlefunc) gtk_idle_remove(trace->idlefunc); if (trace->zeroline_gc) gtk_gc_release(trace->zeroline_gc); if (trace->sampling_gc) gtk_gc_release(trace->sampling_gc); if (trace->pt) g_free(trace->pt); (*GTK_OBJECT_CLASS(scope_parent_class)->finalize)(object); } static gint scope_expose(GtkWidget *widget, GdkEventExpose *event) { Scope *trace; g_return_val_if_fail(widget != NULL, FALSE); g_return_val_if_fail(IS_SCOPE(widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (GTK_WIDGET_DRAWABLE(widget)) { trace = SCOPE(widget); if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, scope_idle_callback, trace); } return FALSE; } static void scope_draw(Scope *trace) { GdkGCValues gc_values; guint totx, idx, i, si; guint32 mulx, muly, zeroy, x; GdkPoint p[SCOPEMAXPOINTS+1]; GdkSegment s[SCOPEMAXPOINTS]; /* init gc's if necessary */ if (!trace->zeroline_gc) { if (!gdk_color_alloc(trace->widget.style->colormap, &trace->zerolinecol)) g_warning("unable to allocate color: ( %d %d %d )", trace->zerolinecol.red, trace->zerolinecol.green, trace->zerolinecol.blue); gc_values.foreground = trace->zerolinecol; trace->zeroline_gc = gtk_gc_get(trace->widget.style->depth, trace->widget.style->colormap, &gc_values, GDK_GC_FOREGROUND); } if (!trace->sampling_gc) { if (!gdk_color_alloc(trace->widget.style->colormap, &trace->samplingcol)) g_warning("unable to allocate color: ( %d %d %d )", trace->samplingcol.red, trace->samplingcol.green, trace->samplingcol.blue); gc_values.foreground = trace->samplingcol; trace->sampling_gc = gtk_gc_get(trace->widget.style->depth, trace->widget.style->colormap, &gc_values, GDK_GC_FOREGROUND); } /* first determine the total x size and calculate multiplication factors */ for (totx = 0, idx = trace->ptptr, i = 1; i < trace->ptnum; i++) { idx++; if (idx >= trace->ptnum) idx = 0; totx += trace->pt[idx].dx & 0x7fff; } mulx = (((guint32)trace->widget.allocation.width) << 16) / MAX(totx, 1); muly = trace->widget.allocation.height; zeroy = (0x8000 * muly) >> 16; /* prepare sampling ticks and lines */ for (si = x = i = 0, idx = trace->ptptr; i < trace->ptnum; i++) { p[i].x = trace->widget.allocation.x + ((x * mulx) >> 16); p[i].y = trace->widget.allocation.y + ((trace->pt[idx].y * muly) >> 16); if (trace->pt[idx].dx & 0x8000) { s[si].x1 = s[si].x2 = p[i].x; s[si].y1 = trace->widget.allocation.y + zeroy - 4; s[si].y2 = trace->widget.allocation.y + zeroy + 4; si++; } idx++; if (idx >= trace->ptnum) idx = 0; x += trace->pt[idx].dx & 0x7fff; } /* now draw everything */ gdk_draw_rectangle(trace->widget.window, trace->widget.style->base_gc[trace->widget.state], TRUE, trace->widget.allocation.x, trace->widget.allocation.y, trace->widget.allocation.x+trace->widget.allocation.width, trace->widget.allocation.y+trace->widget.allocation.height); gdk_draw_line(trace->widget.window, trace->zeroline_gc, trace->widget.allocation.x, trace->widget.allocation.y+zeroy, trace->widget.allocation.x+trace->widget.allocation.width-1, trace->widget.allocation.y+zeroy); gdk_draw_segments(trace->widget.window, trace->sampling_gc, s, si); gdk_draw_lines(trace->widget.window, trace->widget.style->fg_gc[trace->widget.state], p, trace->ptnum); } static gint scope_idle_callback(gpointer data) { g_return_val_if_fail(data != NULL, FALSE); g_return_val_if_fail(IS_SCOPE(data), FALSE); SCOPE(data)->idlefunc = 0; if (!GTK_WIDGET_DRAWABLE(GTK_WIDGET(data))) return FALSE; if (!SCOPE(data)->pt || !SCOPE(data)->ptnum) return FALSE; scope_draw(SCOPE(data)); return FALSE; /* don't call this callback again */ } void scope_addvalue(Scope *trace, guint16 deltax, gint16 y, guint flags) { g_return_if_fail(trace != NULL); g_return_if_fail(IS_SCOPE(trace)); trace->pt[trace->ptptr].y = 32767-y; trace->pt[trace->ptptr].dx = deltax & 0x7fff; if (flags) trace->pt[trace->ptptr].dx |= 0x8000; trace->ptptr++; if (trace->ptptr >= trace->ptnum) trace->ptptr = 0; if (GTK_WIDGET_DRAWABLE(GTK_WIDGET(trace))) { if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, scope_idle_callback, trace); } } void scope_addvalues(Scope *trace, guint num, guint16 *deltax, gint16 *y) { guint i; g_return_if_fail(trace != NULL); g_return_if_fail(IS_SCOPE(trace)); g_return_if_fail(y != NULL); for (i = 0; i < num; i++) { trace->pt[trace->ptptr].y = 32767-y[i]; trace->pt[trace->ptptr].dx = deltax ? (deltax[i] & 0x7fff) : 1; trace->ptptr++; if (trace->ptptr >= trace->ptnum) trace->ptptr = 0; } if (GTK_WIDGET_DRAWABLE(GTK_WIDGET(trace))) { if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, scope_idle_callback, trace); } } /* ---------------------------------------------------------------------- */ /* * This fft routine is from ~gabriel/src/filters/fft/fft.c; * I am unsure of the original source. The file contains no * copyright notice or description. * The declaration is changed to the prototype form but the * function body is unchanged. (J. T. Buck) */ #define SWAP(a, b) tempr=(a); (a)=(b); (b)=tempr /* * Replace data by its discrete Fourier transform, if isign is * input as 1, or by its inverse discrete Fourier transform, if * "isign" is input as -1. "data'"is a complex array of length "nn", * input as a real array data[0..2*nn-1]. "nn" MUST be an integer * power of 2 (this is not checked for!?) */ static void fft_rif(float *data, int nn, int isign) { int n; int mmax; int m, j, istep, i; float wtemp, wr, wpr, wpi, wi, theta; float tempr, tempi; data--; n = nn << 1; j = 1; for (i = 1; i < n; i += 2) { if(j > i) { SWAP(data[j], data[i]); SWAP(data[j+1], data[i+1]); } m= n >> 1; while (m >= 2 && j >m) { j -= m; m >>= 1; } j += m; } mmax = 2; while (n > mmax) { istep = 2*mmax; theta = -6.28318530717959/(isign*mmax); wtemp = sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi = sin(theta); wr = 1.0; wi = 0.0; for (m = 1; m < mmax; m += 2) { for (i = m; i < n; i += istep) { j = i + mmax; tempr = wr*data[j] - wi*data[j+1]; tempi = wr*data[j+1] + wi*data[j]; data[j] = data[i] - tempr; data[j+1] = data[i+1] - tempi; data[i] += tempr; data[i+1] += tempi; } wr = (wtemp=wr)*wpr - wi*wpi+wr; wi = wi*wpr + wtemp*wpi + wi; } mmax = istep; } } #undef SWAP static inline float hamming(float x) { return 0.54-0.46*cos(2*M_PI*x); } /* ---------------------------------------------------------------------- */ #define SPECTRUMMAXPOINTS 1024 static void spectrum_class_init(SpectrumClass *klass); static void spectrum_init(Spectrum *trace); static void spectrum_finalize(GtkObject *object); static gint spectrum_expose(GtkWidget *widget, GdkEventExpose *event); static void spectrum_size_request(GtkWidget *widget, GtkRequisition *requisition); static gint spectrum_idle_callback(gpointer data); static GtkWidgetClass *spectrum_parent_class = NULL; static SpectrumClass *spectrum_class = NULL; guint spectrum_get_type(void) { static guint trace_type = 0; if (!trace_type) { static const GtkTypeInfo trace_info = { "Spectrum", sizeof(Spectrum), sizeof(SpectrumClass), (GtkClassInitFunc) spectrum_class_init, (GtkObjectInitFunc) spectrum_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; trace_type = gtk_type_unique(gtk_widget_get_type(), &trace_info); } return trace_type; } static void spectrum_class_init(SpectrumClass *klass) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*)klass; widget_class = (GtkWidgetClass*)klass; spectrum_parent_class = gtk_type_class(gtk_widget_get_type()); spectrum_class = klass; object_class->finalize = spectrum_finalize; widget_class->size_request = spectrum_size_request; widget_class->expose_event = spectrum_expose; } static void spectrum_init(Spectrum *trace) { GTK_WIDGET_SET_FLAGS(trace, GTK_NO_WINDOW); trace->idlefunc = 0; /* initialize the colors */ trace->gridcol.red = 65535; trace->gridcol.green = 13107; trace->gridcol.blue = 0; trace->samplingcol.red = 13763; trace->samplingcol.green = 42598; trace->samplingcol.blue = 5243; trace->grid_gc = trace->sampling_gc = NULL; /* initialize the trace history */ trace->ptnum = trace->ptptr = 0; trace->pt = NULL; } static void spectrum_size_request(GtkWidget *widget, GtkRequisition *requisition) { Spectrum *trace; g_return_if_fail(widget != NULL); g_return_if_fail(IS_SPECTRUM(widget)); g_return_if_fail(requisition != NULL); trace = SPECTRUM(widget); requisition->width = MIN(trace->ptnum * 4, 512); requisition->height = 256; } GtkWidget* spectrum_new(char *name, char *str1, char *str2, gint int1, gint int2) { guint i, j; Spectrum *trace; trace = gtk_type_new(spectrum_get_type()); i = CLAMP(int1, 4, SPECTRUMMAXPOINTS); j = 0; while (i > 1) { i >>= 1; j++; } trace->ptnum = 1 << j; trace->pt = g_malloc0(trace->ptnum * sizeof(gint16)); return GTK_WIDGET(trace); } static void spectrum_finalize(GtkObject *object) { Spectrum *trace; g_return_if_fail(object != NULL); g_return_if_fail(IS_SPECTRUM(object)); trace = SPECTRUM(object); if (trace->idlefunc) gtk_idle_remove(trace->idlefunc); if (trace->grid_gc) gtk_gc_release(trace->grid_gc); if (trace->sampling_gc) gtk_gc_release(trace->sampling_gc); if (trace->pt) g_free(trace->pt); (*GTK_OBJECT_CLASS(spectrum_parent_class)->finalize)(object); } static gint spectrum_expose(GtkWidget *widget, GdkEventExpose *event) { Spectrum *trace; g_return_val_if_fail(widget != NULL, FALSE); g_return_val_if_fail(IS_SPECTRUM(widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (GTK_WIDGET_DRAWABLE(widget)) { trace = SPECTRUM(widget); if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, spectrum_idle_callback, trace); } return FALSE; } static void spectrum_draw(Spectrum *trace) { GdkGCValues gc_values; guint idx, i; guint32 mulx; gint y; GdkPoint p[SPECTRUMMAXPOINTS/2+1]; float f[2*SPECTRUMMAXPOINTS]; float ftmp1, ftmp2, ftmp3; /* init gc's if necessary */ if (!trace->grid_gc) { if (!gdk_color_alloc(trace->widget.style->colormap, &trace->gridcol)) g_warning("unable to allocate color: ( %d %d %d )", trace->gridcol.red, trace->gridcol.green, trace->gridcol.blue); gc_values.foreground = trace->gridcol; trace->grid_gc = gtk_gc_get(trace->widget.style->depth, trace->widget.style->colormap, &gc_values, GDK_GC_FOREGROUND); } if (!trace->sampling_gc) { if (!gdk_color_alloc(trace->widget.style->colormap, &trace->samplingcol)) g_warning("unable to allocate color: ( %d %d %d )", trace->samplingcol.red, trace->samplingcol.green, trace->samplingcol.blue); gc_values.foreground = trace->samplingcol; trace->sampling_gc = gtk_gc_get(trace->widget.style->depth, trace->widget.style->colormap, &gc_values, GDK_GC_FOREGROUND); } /* first determine the total x size and calculate multiplication factors */ ftmp1 = 1.0 / (float)trace->ptnum; for (idx = trace->ptptr, i = 0; i < trace->ptnum; i++) { f[2*i] = trace->pt[idx] * hamming((float)i * ftmp1); f[2*i+1] = 0; idx++; if (idx >= trace->ptnum) idx = 0; } fft_rif(f, trace->ptnum, 1); mulx = (((guint32)trace->widget.allocation.width) << 17) / trace->ptnum; /* prepare sampling ticks and lines */ ftmp2 = trace->widget.allocation.height * 0.0723824136504; /* 1/ln(10^6) */ ftmp3 = 21.4875015613 + 4.6; for (i = 0; i < trace->ptnum / 2; i++) { ftmp1 = f[2*i] * f[2*i] + f[2*i+1] * f[2*i+1]; if (ftmp1 < 2168) p[i].y = trace->widget.allocation.y + trace->widget.allocation.height - 1; else { y = ftmp2 * (ftmp3 - log(ftmp1)); y = CLAMP(y, 0, trace->widget.allocation.height-1); p[i].y = trace->widget.allocation.y + y; } p[i].x = trace->widget.allocation.x + ((i * mulx) >> 16); } /* now draw everything */ gdk_draw_rectangle(trace->widget.window, trace->widget.style->base_gc[trace->widget.state], TRUE, trace->widget.allocation.x, trace->widget.allocation.y, trace->widget.allocation.x+trace->widget.allocation.width, trace->widget.allocation.y+trace->widget.allocation.height); for (i = 0; i < 3; i++) gdk_draw_line(trace->widget.window, trace->grid_gc, trace->widget.allocation.x, trace->widget.allocation.y+i*(trace->widget.allocation.height/3), trace->widget.allocation.x+trace->widget.allocation.width-1, trace->widget.allocation.y+i*(trace->widget.allocation.height/3)); gdk_draw_line(trace->widget.window, trace->grid_gc, trace->widget.allocation.x, trace->widget.allocation.y+trace->widget.allocation.height-1, trace->widget.allocation.x+trace->widget.allocation.width-1, trace->widget.allocation.y+trace->widget.allocation.height-1); gdk_draw_lines(trace->widget.window, trace->widget.style->fg_gc[trace->widget.state], p, trace->ptnum / 2); } static gint spectrum_idle_callback(gpointer data) { g_return_val_if_fail(data != NULL, FALSE); g_return_val_if_fail(IS_SPECTRUM(data), FALSE); SPECTRUM(data)->idlefunc = 0; if (!GTK_WIDGET_DRAWABLE(GTK_WIDGET(data))) return FALSE; if (!SPECTRUM(data)->pt || !SPECTRUM(data)->ptnum) return FALSE; spectrum_draw(SPECTRUM(data)); return FALSE; /* don't call this callback again */ } void spectrum_addvalue(Spectrum *trace, gint16 y) { g_return_if_fail(trace != NULL); g_return_if_fail(IS_SPECTRUM(trace)); trace->pt[trace->ptptr] = y; trace->ptptr++; if (trace->ptptr >= trace->ptnum) trace->ptptr = 0; if (GTK_WIDGET_DRAWABLE(GTK_WIDGET(trace))) { if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, spectrum_idle_callback, trace); } } void spectrum_addvalues(Spectrum *trace, guint num, gint16 *y) { guint i; g_return_if_fail(trace != NULL); g_return_if_fail(IS_SPECTRUM(trace)); g_return_if_fail(y != NULL); for (i = 0; i < num; i++) { trace->pt[trace->ptptr] = y[i]; trace->ptptr++; if (trace->ptptr >= trace->ptnum) trace->ptptr = 0; } if (GTK_WIDGET_DRAWABLE(GTK_WIDGET(trace))) { if (!trace->idlefunc) trace->idlefunc = gtk_idle_add_priority(PRIO, spectrum_idle_callback, trace); } }