3 * gt2 spectrum analyzer
5 * Copyright (C) 2004 Monty
7 * This analyzer 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, or (at your option)
12 * The analyzer 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 Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <gdk/gdkkeysyms.h>
31 static GtkDrawingAreaClass
*parent_class
= NULL
;
33 static void compute_imp_scale(GtkWidget
*widget
){
35 int height
=widget
->allocation
.height
-p
->pady
;
37 double lfreqs
[9]={10000000.,1000000.,100000.,10000.,1000.,100.,10.,1.,.1};
38 double tfreqs
[64]={9000000.,8000000.,7000000.,6000000.,
39 5000000.,4000000.,3000000.,2000000.,
40 900000.,800000.,700000.,600000.,
41 500000.,400000.,300000.,200000.,
42 90000.,80000.,70000.,60000.,
43 50000.,40000.,30000.,20000.,
44 9000.,8000.,7000.,6000.,
45 5000.,4000.,3000.,2000.,
56 p
->ygrid
[i
]=rint( (log10(p
->disp_ymax
)-log10(lfreqs
[i
]))/(log10(p
->disp_ymax
)-log10(.1)) * (height
-1));
58 p
->ytic
[i
]=rint( (log10(p
->disp_ymax
)-log10(tfreqs
[i
]))/(log10(p
->disp_ymax
)-log10(.1)) * (height
-1));
64 static void compute_metadata(GtkWidget
*widget
){
66 int width
=widget
->allocation
.width
-p
->padx
;
69 /* find the places to plot the x grid lines according to scale */
73 double lfreqs
[6]={1.,10.,100.,1000.,10000.,100000};
74 double tfreqs
[37]={5.,6.,7.,8.,9.,20.,30.,40.,50.,60.,70.,80.,90.
75 ,200.,300.,400.,500.,600.,700.,800.,900.,
76 2000.,3000.,4000.,5000.,6000.,7000.,8000.,9000.,
77 20000.,30000,40000,50000,60000,70000,80000,90000};
79 p
->xgrid
[i
]=rint( (log10(lfreqs
[i
])-log10(5.))/(log10(100000.)-log10(5.)) * (width
-1))+p
->padx
;
81 p
->xtic
[i
]=rint( (log10(tfreqs
[i
])-log10(5.))/(log10(100000.)-log10(5.)) * (width
-1))+p
->padx
;
89 double lfreqs
[10]={31.,63.,125.,250.,500.,1000.,2000.,4000.,8000.,16000.};
90 double tfreqs
[20]={25.,40.,50.,80.,100.,160.,200.,315.,400.,630.,800.,
91 1250.,1600.,2500.,3150.,5000.,6300.,10000.,12500.,20000.};
93 p
->xgrid
[i
]=rint( (log2(lfreqs
[i
])-log2(25.))/(log2(20000.)-log2(25.)) * (width
-1))+p
->padx
;
95 p
->xtic
[i
]=rint( (log2(tfreqs
[i
])-log2(25.))/(log2(20000.)-log2(25.)) * (width
-1))+p
->padx
;
101 case 2: /* linear; 2kHz spacing */
106 p
->xgrid
[i
]=rint(lfreq
/20000. * (width
-1))+p
->padx
;
111 while((lfreq
=(p
->xtics
+1)*500.)<20000.)
112 p
->xtic
[p
->xtics
++]=rint(lfreq
/20000. * (width
-1))+p
->padx
;
120 GdkColor
chcolor(int ch
){
121 GdkColor rgb
={0,0,0,0};
164 static void draw(GtkWidget
*widget
){
166 Plot
*p
=PLOT(widget
);
167 int height
=widget
->allocation
.height
;
168 int width
=widget
->allocation
.width
;
169 GtkWidget
*parent
=gtk_widget_get_parent(widget
);
170 int impedence
= (p
->link
== LINK_IMPEDENCE_p1
||
171 p
->link
== LINK_IMPEDENCE_1
||
172 p
->link
== LINK_IMPEDENCE_10
);
173 int phase
= (p
->link
== LINK_PHASE
);
177 /* are any of the phase channels actually active? */
182 for(gi
=0;gi
<p
->groups
&& !phase
;gi
++){
183 if(p
->ch_active
[ch
+1]){
193 p
->drawgc
=gdk_gc_new(p
->backing
);
194 gdk_gc_copy(p
->drawgc
,widget
->style
->black_gc
);
197 p
->dashes
=gdk_gc_new(p
->backing
);
198 gdk_gc_copy(p
->dashes
, p
->drawgc
);
199 gdk_gc_set_line_attributes(p
->dashes
, 1, GDK_LINE_ON_OFF_DASH
, GDK_CAP_BUTT
, GDK_JOIN_MITER
);
200 gdk_gc_set_dashes(p
->dashes
,0,"\002\002",2);
203 /* clear the old rectangle out */
205 GdkGC
*gc
=parent
->style
->bg_gc
[0];
206 gdk_draw_rectangle(p
->backing
,gc
,1,0,0,padx
,height
);
207 gdk_draw_rectangle(p
->backing
,gc
,1,0,height
-p
->pady
,width
,p
->pady
);
209 gc
=parent
->style
->white_gc
;
210 gdk_draw_rectangle(p
->backing
,gc
,1,padx
,0,width
-padx
,height
-p
->pady
+1);
213 /* draw the noise floor if active */
215 GdkColor rgb
= {0,0xd000,0xd000,0xd000};
216 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
218 for(i
=0;i
<width
-padx
;i
++){
219 float val
=p
->floor
[i
];
222 /* No noise floor is passed back for display in the modes where it's irrelevant */
223 y
= rint((height
-p
->pady
-1)/p
->disp_depth
*(p
->disp_ymax
-val
));
225 gdk_draw_line(p
->backing
,p
->drawgc
,padx
+i
,y
,padx
+i
,height
-p
->pady
-1);
229 /* draw the light x grid */
232 GdkColor rgb
={0,0,0,0};
237 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
239 for(i
=0;i
<p
->xtics
;i
++)
240 gdk_draw_line(p
->backing
,p
->drawgc
,p
->xtic
[i
],0,p
->xtic
[i
],height
-p
->pady
);
243 /* draw the x labels */
245 PangoLayout
**proper
;
248 proper
=p
->log_layout
;
251 proper
=p
->iso_layout
;
254 proper
=p
->lin_layout
;
257 for(i
=0;i
<p
->xgrids
;i
++){
259 pango_layout_get_pixel_size(proper
[i
],&px
,&py
);
261 gdk_draw_layout (p
->backing
,
262 widget
->style
->black_gc
,
263 p
->xgrid
[i
]-(px
/2), height
-py
+2,
268 /* draw the light y grid */
269 if(impedence
){ /* impedence mode */
271 GdkColor rgb
={0,0,0,0};
275 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
277 compute_imp_scale(widget
);
279 for(i
=0;i
<p
->ytics
;i
++)
280 gdk_draw_line(p
->backing
,p
->drawgc
,padx
,p
->ytic
[i
],width
,p
->ytic
[i
]);
283 float del
=(height
-p
->pady
-1)/(float)p
->disp_depth
,off
;
286 GdkColor rgb
={0,0,0,0};
293 off
=(p
->disp_ymax
-ceil(p
->disp_ymax
))*2;
298 off
=p
->disp_ymax
-ceil(p
->disp_ymax
);
302 off
=p
->disp_ymax
-ceil(p
->disp_ymax
*.5)*2;
306 off
=p
->disp_ymax
-ceil(p
->disp_ymax
*.2)*5;
312 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
313 gdk_gc_set_rgb_fg_color(p
->dashes
,&rgb
);
316 int ymid
=rint(del
* (i
* mul
+ off
));
317 if(ymid
>=0 && ymid
<height
-p
->pady
)
318 gdk_draw_line(p
->backing
,p
->drawgc
,padx
,ymid
,width
,ymid
);
322 int ymid
=rint(del
* (i
* mul
+ off
));
323 if(ymid
>=0 && ymid
<height
-p
->pady
)
324 gdk_draw_line(p
->backing
,(half
?p
->dashes
:p
->drawgc
),padx
,ymid
,width
,ymid
);
332 GdkColor rgb
={0,0,0,0};
337 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
339 for(i
=0;i
<p
->xgrids
;i
++)
340 gdk_draw_line(p
->backing
,p
->drawgc
,p
->xgrid
[i
],0,p
->xgrid
[i
],height
-p
->pady
);
345 GdkColor rgb
={0,0,0,0};
350 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
352 for(i
=0;i
<p
->ygrids
;i
++){
354 pango_layout_get_pixel_size(p
->imp_layout
[i
],&px
,&py
);
356 gdk_draw_layout (p
->backing
,
357 widget
->style
->black_gc
,
358 padx
-px
-2, p
->ygrid
[i
]-py
/2,
361 gdk_draw_line(p
->backing
,p
->drawgc
,padx
,p
->ygrid
[i
],width
,p
->ygrid
[i
]);
365 GdkColor rgb
={0,0,0,0};
366 int label
=ceil(p
->disp_ymax
/5+28),i
;
367 float del
=(height
-p
->pady
-1)/(float)p
->disp_depth
,step
;
368 float off
=p
->disp_ymax
-ceil(p
->disp_ymax
*.2)*5;
373 if(((label
-i
)&1)==0 || step
==1){
374 int ymid
=rint(del
* (i
*5+off
));
377 if(((label
-i
)&1)==0){
386 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
388 if(label
-i
>=0 && label
-i
<57 && ymid
>=0 && ymid
<height
-p
->pady
){
389 pango_layout_get_pixel_size(p
->db_layout
[label
-i
],&px
,&py
);
391 gdk_draw_layout (p
->backing
,
392 widget
->style
->black_gc
,
393 padx
-px
-2, ymid
-py
/2,
394 p
->db_layout
[label
-i
]);
395 gdk_draw_line(p
->backing
,p
->drawgc
,padx
,ymid
,width
,ymid
);
401 /* phase? draw in phase and tics on right axis */
403 GdkColor rgb
={0,0xd000,0x0000,0x0000};
404 float depth
= p
->disp_pmax
-p
->disp_pmin
;
405 int label
=ceil(p
->disp_pmax
/10+18),i
;
406 float del
=(height
-p
->pady
-1)/depth
,step
;
407 float off
=p
->disp_pmax
-ceil(p
->disp_pmax
*.1)*10;
411 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
413 if(((label
-i
)&1)==0 || step
==1){
414 int ymid
=rint(del
* (i
*10+off
));
417 if(label
-i
>=0 && label
-i
<37 && ymid
>=0 && ymid
<height
-p
->pady
){
418 pango_layout_get_pixel_size(p
->phase_layout
[label
-i
],&px
,&py
);
420 gdk_draw_layout (p
->backing
,p
->drawgc
,
421 width
-p
->phax
, ymid
-py
/2,
422 p
->phase_layout
[label
-i
]);
429 int ymid
=rint(del
* (i
+off
));
430 if(ymid
>=height
-p
->pady
)break;
432 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-(i
%5==0?15:10),ymid
,width
-p
->phax
-(i
%5==0?5:7),ymid
);
436 int ymid
=rint(del
* (i
*2+off
));
437 if(ymid
>=height
-p
->pady
)break;
439 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-12,ymid
,width
-p
->phax
-7,ymid
);
443 int ymid
=rint(del
* (i
*5+off
));
444 if(ymid
>=height
-p
->pady
)break;
446 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-15,ymid
,width
-p
->phax
-5,ymid
);
451 int ymid
=rint(del
* (i
*10+off
));
452 if(ymid
>=height
-p
->pady
)break;
454 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-5,ymid
-1,width
-p
->phax
-2,ymid
-1);
455 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-25,ymid
,width
-p
->phax
-2,ymid
);
456 gdk_draw_line(p
->backing
,p
->drawgc
,width
-p
->phax
-5,ymid
+1,width
-p
->phax
-2,ymid
+1);
462 /* draw actual data */
466 for(gi
=0;gi
<p
->groups
;gi
++){
470 for(ch
=cho
;ch
<cho
+p
->ch
[gi
];ch
++){
471 if(p
->ch_active
[ch
]){
477 gdk_gc_set_rgb_fg_color(p
->drawgc
,&rgb
);
479 for(i
=0;i
<width
-padx
;i
++){
480 float val
=p
->ydata
[ch
][i
];
483 if(isnan(yprev
) || isnan(val
)){
488 if(impedence
){ /* log scale for impedence */
489 y
=rint( (log10(p
->disp_ymax
)-log10(val
))/(log10(p
->disp_ymax
)-log10(.1)) *
491 }else if(phase
&& ch
==cho
+1){
492 y
= rint((height
-p
->pady
-1)/(p
->disp_pmax
-p
->disp_pmin
)*(p
->disp_pmax
-val
));
494 y
= rint((height
-p
->pady
-1)/p
->disp_depth
*(p
->disp_ymax
-val
));
497 if(first
&& (y
<height
-p
->pady
|| prev
<height
-p
->pady
)){
501 if(ly
>=height
-p
->pady
)ly
=height
-p
->pady
;
502 if(lp
>=height
-p
->pady
)lp
=height
-p
->pady
;
504 gdk_draw_line(p
->backing
,p
->drawgc
,padx
+i
-1,lp
,padx
+i
,ly
);
509 if(ly
>=height
-p
->pady
)ly
=height
-p
->pady
;
510 if(lp
>=height
-p
->pady
)lp
=height
-p
->pady
;
512 gdk_draw_line(p
->backing
,p
->drawgc
,padx
+i
-1,lp
,padx
+i
,ly
);
527 static void draw_and_expose(GtkWidget
*widget
){
528 Plot
*p
=PLOT(widget
);
529 if(!GDK_IS_DRAWABLE(p
->backing
))return;
531 if(!GTK_WIDGET_DRAWABLE(widget
))return;
532 if(!GDK_IS_DRAWABLE(widget
->window
))return;
533 gdk_draw_drawable(widget
->window
,
534 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
538 widget
->allocation
.width
,
539 widget
->allocation
.height
);
542 static gboolean
expose( GtkWidget
*widget
, GdkEventExpose
*event
){
543 Plot
*p
=PLOT(widget
);
544 gdk_draw_drawable(widget
->window
,
545 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
547 event
->area
.x
, event
->area
.y
,
548 event
->area
.x
, event
->area
.y
,
549 event
->area
.width
, event
->area
.height
);
554 static void size_request (GtkWidget
*widget
,GtkRequisition
*requisition
){
555 Plot
*p
=PLOT(widget
);
556 requisition
->width
= 800;
557 requisition
->height
= 440;
558 int axisy
=0,axisx
=0,pady
=0,padx
=0,phax
=0,px
,py
,i
;
560 /* find max lin layout */
563 for(i
=0;p
->lin_layout
[i
];i
++){
564 pango_layout_get_pixel_size(p
->lin_layout
[i
],&px
,&py
);
569 if(axisx
<max
)axisx
=max
;
571 /* find max log layout */
574 for(i
=0;p
->log_layout
[i
];i
++){
575 pango_layout_get_pixel_size(p
->log_layout
[i
],&px
,&py
);
580 if(axisx
<max
)axisx
=max
;
582 /* find max iso layout */
585 for(i
=0;p
->iso_layout
[i
];i
++){
586 pango_layout_get_pixel_size(p
->iso_layout
[i
],&px
,&py
);
591 if(axisx
<max
)axisx
=max
;
593 /* find max db layout */
596 for(i
=0;p
->db_layout
[i
];i
++){
597 pango_layout_get_pixel_size(p
->db_layout
[i
],&px
,&py
);
602 if(axisy
<max
)axisx
=max
;
604 /* find max imped layout */
607 for(i
=0;p
->imp_layout
[i
];i
++){
608 pango_layout_get_pixel_size(p
->imp_layout
[i
],&px
,&py
);
613 if(axisy
<max
)axisx
=max
;
615 /* find max phase layout */
618 for(i
=0;p
->phase_layout
[i
];i
++){
619 pango_layout_get_pixel_size(p
->phase_layout
[i
],&px
,&py
);
624 if(axisy
<max
)axisx
=max
;
627 if(requisition
->width
<axisx
+padx
)requisition
->width
=axisx
+padx
;
628 if(requisition
->height
<axisy
+pady
)requisition
->height
=axisy
+pady
;
634 static gboolean
configure(GtkWidget
*widget
, GdkEventConfigure
*event
){
635 Plot
*p
=PLOT(widget
);
638 g_object_unref(p
->backing
);
640 p
->backing
= gdk_pixmap_new(widget
->window
,
641 widget
->allocation
.width
,
642 widget
->allocation
.height
,
648 compute_metadata(widget
);
649 plot_refresh(p
,NULL
);
650 draw_and_expose(widget
);
655 static void plot_class_init (PlotClass
*class){
656 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS (class);
657 parent_class
= g_type_class_peek_parent (class);
659 widget_class
->expose_event
= expose
;
660 widget_class
->configure_event
= configure
;
661 widget_class
->size_request
= size_request
;
664 static void plot_init (Plot
*p
){
670 GType
plot_get_type (void){
671 static GType m_type
= 0;
673 static const GTypeInfo m_info
={
675 NULL
, /* base_init */
676 NULL
, /* base_finalize */
677 (GClassInitFunc
) plot_class_init
,
678 NULL
, /* class_finalize */
679 NULL
, /* class_data */
682 (GInstanceInitFunc
) plot_init
,
686 m_type
= g_type_register_static (GTK_TYPE_DRAWING_AREA
, "Plot", &m_info
, 0);
692 GtkWidget
* plot_new (int size
, int groups
, int *channels
, int *rate
){
693 GtkWidget
*ret
= GTK_WIDGET (g_object_new (plot_get_type (), NULL
));
699 for(g
=0;g
<groups
;g
++)
706 /* generate all the text layouts we'll need */
708 char *labels
[11]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
709 "14kHz","16kHz","18kHz",""};
710 p
->lin_layout
=calloc(12,sizeof(*p
->lin_layout
));
712 p
->lin_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
715 char *labels
[37]={"-180\xC2\xB0","-170\xC2\xB0","-160\xC2\xB0",
716 "-150\xC2\xB0","-140\xC2\xB0","-130\xC2\xB0",
717 "-120\xC2\xB0","-110\xC2\xB0","-100\xC2\xB0",
718 "-90\xC2\xB0","-80\xC2\xB0","-70\xC2\xB0",
719 "-60\xC2\xB0","-50\xC2\xB0","-40\xC2\xB0",
720 "-30\xC2\xB0","-20\xC2\xB0","-10\xC2\xB0",
721 "-0\xC2\xB0","+10\xC2\xB0","+20\xC2\xB0",
722 "+30\xC2\xB0","+40\xC2\xB0","+50\xC2\xB0",
723 "+60\xC2\xB0","+70\xC2\xB0","+80\xC2\xB0",
724 "+90\xC2\xB0","+100\xC2\xB0","+110\xC2\xB0",
725 "+120\xC2\xB0","+130\xC2\xB0","+140\xC2\xB0",
726 "+150\xC2\xB0","+160\xC2\xB0","+170\xC2\xB0",
728 p
->phase_layout
=calloc(38,sizeof(*p
->phase_layout
));
730 p
->phase_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
733 char *labels
[6]={"1Hz","10Hz","100Hz","1kHz","10kHz",""};
734 p
->log_layout
=calloc(7,sizeof(*p
->log_layout
));
736 p
->log_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
739 char *labels
[9]={"10M\xCE\xA9","1M\xCE\xA9","100k\xCE\xA9","10k\xCE\xA9",
740 "1k\xCE\xA9","100\xCE\xA9","10\xCE\xA9","1\xCE\xA9",".1\xCE\xA9"};
741 p
->imp_layout
=calloc(10,sizeof(*p
->imp_layout
));
743 p
->imp_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
746 char *labels
[10]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
747 "4kHz","8kHz","16kHz"};
748 p
->iso_layout
=calloc(11,sizeof(*p
->iso_layout
));
750 p
->iso_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
753 char *labels
[57]={"-140dB","-135dB","-130dB","-125dB","-120dB","-115dB",
754 "-110dB","-105dB","-100dB","-95dB","-90dB","-85dB",
755 "-80dB","-75dB","-70dB","-65dB","-60dB","-55dB","-50dB",
756 "-45dB","-40dB","-35dB","-30dB","-25dB","-20dB",
757 "-15dB","-10dB","-5dB","0dB","+5dB","+10dB","+15dB",
758 "+20dB","+25dB","+30dB","+35dB","+40dB","+45dB","+50dB",
759 "+55dB","+60dB","+65dB","+70dB","+75dB","+80dB","+85dB",
760 "+90dB","+95dB","+100dB","+105dB","+110dB","+115dB",
761 "+120dB","+125dB","+130dB","+135dB","+140dB"};
762 p
->db_layout
=calloc(58,sizeof(*p
->db_layout
));
764 p
->db_layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
767 p
->ch_active
=calloc(ch
,sizeof(*p
->ch_active
));
768 p
->ch_process
=calloc(ch
,sizeof(*p
->ch_process
));
774 void plot_refresh (Plot
*p
, int *process
){
775 float ymax
,pmax
,pmin
;
776 int width
=GTK_WIDGET(p
)->allocation
.width
-p
->padx
;
777 int height
=GTK_WIDGET(p
)->allocation
.height
-p
->pady
;
781 if(!p
->configured
)return;
784 memcpy(p
->ch_process
,process
,p
->total_ch
*sizeof(*process
));
786 data
= process_fetch(p
->res
, p
->scale
, p
->mode
, p
->link
,
787 p
->ch_process
,width
,&ymax
,&pmax
,&pmin
,&floor
,p
->noise
);
795 /* graph limit updates are conditional depending on mode/link */
799 if(pmin
>-30)pmin
=-30;
802 case LINK_INDEPENDENT
:
808 float dBpp
= p
->depth
/height
;
812 case LINK_IMPEDENCE_p1
:
813 case LINK_IMPEDENCE_1
:
814 case LINK_IMPEDENCE_10
:
823 /* "Instantaneous' mode scale regression is conditional and
824 damped. Start the timer/run the timer while any one scale measure
825 should be dropping by more than 50px. If any peaks occur above,
826 reset timer. Once timer runs out, drop 5px per frame */
829 #define TIMERFRAMES 20
831 float oldzero
= (height
-1)/p
->depth
*p
->ymax
;
832 float newzero
= (height
-1)/p
->depth
*ymax
;
834 if(newzero
+PXTHRESH
<oldzero
){
838 p
->ymax
= (oldzero
-PXDEL
)*p
->depth
/(height
-1);
841 p
->ymaxtimer
= TIMERFRAMES
;
844 p
->ymaxtimer
= TIMERFRAMES
;
846 if(p
->pmax
>pmax
|| p
->pmin
<pmin
){
847 float newmax
= (height
-1)/(p
->pmax
-p
->pmin
)*(p
->pmax
-pmax
);
848 float newmin
= (height
-1)/(p
->pmax
-p
->pmin
)*(pmin
-p
->pmin
);
850 if(newmax
>PXTHRESH
|| newmin
>PXTHRESH
){
855 p
->pmax
-= PXDEL
/(height
-1)*(p
->pmax
-p
->pmin
);
857 p
->pmin
+= PXDEL
/(height
-1)*(p
->pmax
-p
->pmin
);
860 p
->phtimer
= TIMERFRAMES
;
863 p
->phtimer
= TIMERFRAMES
;
866 if(ymax
<p
->depth
-140.)ymax
=p
->depth
-140.;
867 if(ymax
>140.)ymax
=140.;
868 if(pmax
>180)pmax
=180;
869 if(pmin
<-180)pmin
=-180;
872 if(ymax
>p
->ymax
)p
->ymax
=ymax
;
873 if(pmax
>p
->pmax
)p
->pmax
=pmax
;
874 if(pmin
<p
->pmin
)p
->pmin
=pmin
;
881 p
->disp_depth
= p
->depth
;
882 p
->disp_ymax
= p
->ymax
;
883 p
->disp_pmax
= p
->pmax
;
884 p
->disp_pmin
= p
->pmin
;
886 /* finally, align phase/response zeros on phase graphs */
887 if(p
->disp_ymax
>-140){
888 if(p
->link
== LINK_PHASE
){
889 /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
890 float mzero
= (height
-1)/p
->disp_depth
*p
->disp_ymax
;
891 float pzero
= (height
-1)/(p
->disp_pmax
-p
->disp_pmin
)*p
->disp_pmax
;
894 /* straightforward; move the dB range down */
895 p
->disp_ymax
= pzero
*p
->disp_depth
/(height
-1);
897 /* a little harder as phase has a min and a max.
898 First increase the pmax to match the dB zero. */
899 p
->disp_pmax
= p
->disp_pmin
/(1-(height
-1)/mzero
);
900 pzero
= (height
-1)/(p
->disp_pmax
-p
->disp_pmin
)*p
->disp_pmax
;
902 /* That worked, but might have run p->max overrange */
903 if(p
->disp_pmax
>180.){
904 /* only way to reconcile this one is to increase the pdepth */
906 pzero
= (height
-1)/(p
->disp_pmax
-p
->disp_pmin
)*p
->disp_pmax
;
907 p
->disp_depth
= (height
-1)/pzero
*p
->disp_ymax
;
914 void plot_clear (Plot
*p
){
915 GtkWidget
*widget
=GTK_WIDGET(p
);
916 int width
=GTK_WIDGET(p
)->allocation
.width
-p
->padx
;
920 for(i
=0;i
<p
->total_ch
;i
++)
923 p
->ymax
=p
->depth
-140;
926 draw_and_expose(widget
);
929 float **plot_get (Plot
*p
){
933 void plot_setting (Plot
*p
, int res
, int scale
, int mode
, int link
, int depth
, int noise
){
934 GtkWidget
*widget
=GTK_WIDGET(p
);
946 compute_metadata(widget
);
947 plot_refresh(p
,NULL
);
948 draw_and_expose(widget
);
951 void plot_draw (Plot
*p
){
952 GtkWidget
*widget
=GTK_WIDGET(p
);
953 draw_and_expose(widget
);
956 void plot_set_active(Plot
*p
, int *a
, int *b
){
957 GtkWidget
*widget
=GTK_WIDGET(p
);
958 memcpy(p
->ch_active
,a
,p
->total_ch
*sizeof(*a
));
959 memcpy(p
->ch_process
,b
,p
->total_ch
*sizeof(*b
));
960 plot_refresh(p
,NULL
);
961 draw_and_expose(widget
);