-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsig.c
563 lines (506 loc) · 16.6 KB
/
sig.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
/*
* Metaann
*
* Copyright (C) 1990-2010 George B. Moody
* Copyright (C) 2014 Benjamin Moody
*
* Based on 'sig.c' from the WAVE package (revised 2 March 2010);
* modified by Benjamin Moody, 22 August 2014.
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wave.h"
#include "gtkwave.h"
static void show_signal_names(), show_signal_baselines();
/* Show_display_list() plots the display list pointed to by its argument. */
static struct display_list *lp_current;
static int highlighted = -1;
int in_siglist(i)
int i;
{
int nsl;
for (nsl = 0; nsl < siglistlen; nsl++)
if (siglist[nsl] == i) return (1);
return (0);
}
static void draw_lines_relative(GdkDrawable *drawable, GdkGC *gc,
const GdkPoint *points, int n)
{
static GdkPoint *array;
static int array_size;
int i;
GdkPoint p0, *p;
if (array_size <= n) {
g_free(array);
array_size = n + 1;
array = g_new(GdkPoint, array_size);
}
array[0].x = array[0].y = 0;
for (i = 0; i < n; i++) {
array[i + 1].x = array[i].x + points[i].x;
array[i + 1].y = array[i].y + points[i].y;
}
gdk_draw_lines(drawable, gc, &array[1], n);
}
static void drawtrace(b, n, ybase, gc, mode)
GdkPoint *b;
int mode, n, ybase;
GdkGC *gc;
{
int j, xn, xp;
GdkPoint *p, *q;
if (ybase == -9999) return;
for (j = 0, p = q = b; j <= n; j++) {
if (j == n || q->y == WFDB_INVALID_SAMPLE) {
if (p < q) {
while (p < q && p->y == WFDB_INVALID_SAMPLE)
p++;
xp = p->x;
xn = p - b;
if (nsamp <= canvas_width) xn *= tscale;
p->x = xn;
p->y += ybase;
draw_lines_relative(gtk_widget_get_window(wave_view), gc, p, q-p);
p->x = xp;
p->y -= ybase;
}
p = ++q;
}
else
++q;
}
}
static void show_display_list(lp)
struct display_list *lp;
{
int i, j, k, xn, xp, in_siglist();
GdkPoint *p, *q;
lp_current = lp;
if (!lp) return;
if (sig_mode == 0)
for (i = 0; i < nsig; i++) {
if (lp->vlist[i])
drawtrace(lp->vlist[i], lp->ndpts, base[i], draw_sig, 0);
}
else if (sig_mode == 1)
for (i = 0; i < siglistlen; i++) {
if (0 <= siglist[i] && siglist[i] < nsig && lp->vlist[siglist[i]])
drawtrace(lp->vlist[siglist[i]], lp->ndpts,base[i],draw_sig,0);
}
else { /* sig_mode == 2 (show valid signals only) */
int j, nvsig;
for (i = nvsig = 0; i < nsig; i++)
if (lp->vlist[i] && vvalid[i]) nvsig++;
for (i = j = 0; i < nsig; i++) {
if (lp->vlist[i] && vvalid[i]) {
base[i] = canvas_height*(2*(j++)+1.)/(2.*nvsig);
drawtrace(lp->vlist[i], lp->ndpts, base[i], draw_sig, 0);
}
else
base[i] = -9999;
}
}
highlighted = -1;
}
void sig_highlight(i)
int i;
{
extern void repaint();
if (!lp_current) return;
if (0 <= highlighted && highlighted < lp_current->nsig) {
if (sig_mode != 1) {
drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
base[highlighted], unhighlight_sig, 1);
drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
base[highlighted], draw_sig, 1);
}
else {
int j;
for (j = 0; j < siglistlen; j++) {
if (siglist[j] == highlighted) {
drawtrace(lp_current->vlist[highlighted],
lp_current->ndpts, base[j], unhighlight_sig, 1);
drawtrace(lp_current->vlist[highlighted],
lp_current->ndpts, base[j], draw_sig, 1);
}
}
}
}
highlighted = i;
if (0 <= highlighted && highlighted < lp_current->nsig) {
if (sig_mode != 1) {
drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
base[highlighted], clear_sig, 1);
drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
base[highlighted], highlight_sig, 1);
}
else {
int j;
for (j = 0; j < siglistlen; j++) {
if (siglist[j] == highlighted) {
drawtrace(lp_current->vlist[highlighted],
lp_current->ndpts, base[j], clear_sig, 1);
drawtrace(lp_current->vlist[highlighted],
lp_current->ndpts, base[j], highlight_sig, 1);
}
}
}
}
}
/* Do_disp() executes a display request. The display will show nsamp samples
of nsig signals, starting at display_start_time. */
void do_disp()
{
char *tp;
int c, i, x0, x1, y0;
struct display_list *lp;
/* This might take a while ... */
/*xv_set(frame, FRAME_BUSY, TRUE, NULL);*/
/* Show the grid if requested. */
show_grid();
/* Make sure that the requested time is reasonable. */
if (display_start_time < 0) display_start_time = 0;
/* Update the panel items that indicate the start and end times. */
tp = wtimstr(display_start_time);
/*set_start_time(tp);*/
while (*tp == ' ') tp++;
y0 = canvas_height - mmy(2);
x0 = mmx(2);
wave_draw_string(gtk_widget_get_window(wave_view),
time_mode == 1 ? draw_ann : draw_sig,
x0, y0, tp, strlen(tp));
tp = wtimstr(display_start_time + nsamp);
/*set_end_time(tp);*/
while (*tp == ' ') tp++;
x1 = canvas_width - wave_text_width(tp, strlen(tp)) - mmx(2);
wave_draw_string(gtk_widget_get_window(wave_view),
time_mode == 1 ? draw_ann : draw_sig,
x1, y0, tp, strlen(tp));
/* Show the annotations, if available. */
show_annotations(display_start_time, nsamp);
/* Get a display list for the requested screen, and show it. */
lp = find_display_list(display_start_time);
show_display_list(lp);
/* If requested, show the signal names. */
if (show_signame) show_signal_names();
/* If requested, show the signal baselines. */
if (show_baseline) show_signal_baselines(lp);
/*xv_set(frame, FRAME_BUSY, FALSE, NULL);*/
}
static int nlists;
static int vlist_size;
/* Get_display_list() obtains storage for display lists from the heap (via
malloc) or by recycling a display list in the cache. Since the screen duration
(nsamp) does not change frequently, get_display_list() calculates the pixel
abscissas when a new display list is obtained from the heap, and recalculates
them only when nsamp has been changed. */
static struct display_list *get_display_list()
{
int i, maxx, x;
static int max_nlists = MAX_DISPLAY_LISTS;
struct display_list *lp = NULL, *lpl;
if (nlists < max_nlists &&
(lp = (struct display_list *)calloc(1, sizeof(struct display_list))))
nlists++;
if (lp == NULL)
switch (nlists) {
case 0: return (NULL);
case 1: lp = first_list; break;
default: lpl = first_list; lp = lpl->next;
while (lp->next) {
lpl = lp;
lp = lp->next;
}
lpl->next = NULL;
break;
}
if (lp->nsig < nsig) {
lp->sb = realloc(lp->sb, nsig * sizeof(int));
lp->vlist = realloc(lp->vlist, nsig * sizeof(GdkPoint *));
for (i = lp->nsig; i < nsig; i++) {
lp->sb[i] = 0;
lp->vlist[i] = NULL;
}
lp->nsig = nsig;
}
if (canvas_width > vlist_size) vlist_size = canvas_width;
for (i = 0; i < nsig; i++) {
if (lp->vlist[i] == NULL) {
if ((lp->vlist[i] =
(GdkPoint *)calloc(vlist_size, sizeof(GdkPoint)))==NULL) {
while (--i >= 0)
free(lp->vlist[i]);
free(lp);
max_nlists = --nlists;
return (get_display_list());
}
}
/* If there are more samples to be shown than addressable x-pixels
in the window, the abscissas are simply the integers from 0 to
the canvas width (and some compression will be needed). If the
largest abscissa is correct, all of the others must also be correct,
and they need not be reset. */
if (nsamp > canvas_width) {
x = maxx = canvas_width - 1;
if (lp->vlist[i][x].x != x) {
int xx;
lp->vlist[i][0].x = 0; /* absolute first abscissa */
for (xx = 1; xx <= x; xx++)
lp->vlist[i][xx].x = 1; /* relative to previous */
}
}
/* Otherwise, no compression is needed, and the abscissas must be
distributed across the window (at intervals > 1 pixel). Again,
if the largest abscissa is correct, all of the others must also
be correct, and no computation is needed. */
else {
x = maxx = nsamp - 1;
if (lp->vlist[i][vlist_size-1].x != (int)(x*tscale)) {
int xp, xpp;
lp->vlist[i][0].x = xp = 0; /* absolute first abscissa */
for (x = 1; x < vlist_size; x++) {
xpp = xp;
xp = x*tscale;
lp->vlist[i][x].x = xp - xpp; /* relative to prev */
}
}
}
}
lp->next = first_list;
lp->npoints = nsamp;
lp->xmax = maxx;
return (first_list = lp);
}
/* Find_display_list() obtains a display list beginning at the sample number
specified by its argument. If such a list (with the correct duration) is
found in the cache, it can be returned immediately. Otherwise, the function
reads the requested segment and determines the pixel ordinates of the
vertices of the polylines for each signal. */
struct display_list *find_display_list(fdl_time)
long fdl_time;
{
int c, i, j, x, x0, y, ymax, ymin;
struct display_list *lp;
GdkPoint *tp;
if (fdl_time < 0L) fdl_time = -fdl_time;
/* If the requested display list is in the cache, return it at once. */
for (lp = first_list; lp; lp = lp->next)
if (lp->start == fdl_time && lp->npoints == nsamp) return (lp);
/* Give up if we can't skip to the requested segment, or if we
can't read at least one sample. */
if ((fdl_time != strtim("i") && isigsettime(fdl_time) < 0) ||
getvec(v0) < 0)
return (NULL);
/* Allocate a new display list; give up if we can't do so. Note
that once the structure has been allocated, we must fill it in
with valid data. */
if ((lp = get_display_list()) == NULL)
return (NULL);
/* Record the starting time. */
lp->start = fdl_time;
/* Set the starting point for each signal. */
for (c = 0; c < nsig; c++) {
vmin[c] = vmax[c] = v0[c];
vvalid[c] = 0;
if (v0[c] == WFDB_INVALID_SAMPLE)
lp->vlist[c][0].y = -1 << 15;
else
lp->vlist[c][0].y = v0[c]*vscale[c];
}
/* If there are more than canvas_width samples to be shown, compress the
data. */
if (nsamp > canvas_width) {
for (i = 1, x0 = 0; i < nsamp && getvec(v) > 0; i++) {
for (c = 0, vvalid[c] = 0; c < nsig; c++) {
if (v[c] != WFDB_INVALID_SAMPLE) {
if (v[c] > vmax[c]) vmax[c] = v[c];
if (v[c] < vmin[c]) vmin[c] = v[c];
vvalid[c] = 1;
}
}
if ((x = i*tscale) > x0) {
x0 = x;
for (c = 0; c < nsig; c++) {
if (vvalid[c]) {
if (vmax[c] - v0[c] > v0[c] - vmin[c])
v0[c] = vmin[c] = vmax[c];
else
v0[c] = vmax[c] = vmin[c];
lp->vlist[c][x0].y = v0[c]*vscale[c];
}
else
lp->vlist[c][x0].y = -1 << 15;
}
}
}
i = x0+1;
}
/* If there are canvas_width or fewer samples to be shown, no compression
is necessary. */
else
for (i = 1; i < nsamp && getvec(v) > 0; i++)
for (c = 0; c < nsig; c++) {
if (v[c] == WFDB_INVALID_SAMPLE)
lp->vlist[c][i].y = -1 << 15;
else
lp->vlist[c][i].y = v[c]*vscale[c];
}
/* Record the number of displayed points. This may be less than
expected at the end of the record. */
lp->ndpts = i;
/* Set the y-offset so that the signal will be vertically centered about
the nominal baseline if the midrange is near the mean. The y-offset
is actually a weighted sum of the midrange and the mean, which favors
the mean if the two values differ significantly. */
for (c = 0; c < nsig; c++) {
int dy; /* the y-offset */
int n; /* the number of valid ordinates */
int ymid; /* the midpoint of the ordinate range */
long ymean; /* the mean of the ordinates */
double w; /* weight assigned to ymean in y-offset calculation */
tp = lp->vlist[c];
/* Find the first valid sample in the trace, if any. */
for (j = 0; j < i && tp[j].y == WFDB_INVALID_SAMPLE; j++)
;
ymean = ymax = ymin = (j < i) ? tp[j].y : 0;
for (n = 1; j < i; j++) {
if ((y = tp[j].y) != WFDB_INVALID_SAMPLE) {
if (y > ymax) ymax = y;
else if (y < ymin) ymin = y;
ymean += y;
n++;
}
}
ymean /= n;
ymid = (ymax + ymin)/2;
/* Since ymin <= ymid <= ymax, the next lines imply 0 <= w <= 1 */
if (ymid > ymean) /* in this case, ymax must be > ymean */
w = (ymid - ymean)/(ymax - ymean);
else if (ymid < ymean) /* in this case, ymin must be < ymean */
w = (ymean - ymid)/(ymean - ymin);
else w = 1.0;
dy = -(ymid + ((double)ymean-ymid)*w);
for (j = i-1; j >= 0; j--) {
if (tp[j].y == WFDB_INVALID_SAMPLE) continue;
/* The bounds-checking below shouldn't be necessary (the X server
should clip at the canvas boundaries), but Sun's X11/NeWS
server will crash (and may bring the system down with it) if
run on a system with the GX accelerator and if passed
sufficiently out-of-bounds ordinates (maybe abscissas, too --
this hasn't been tested). This bug is present in both the
original X11/NeWS server and the GFX revision. It appears that
the bug can be avoided if the maximum distance from the window
edge to the out-of-bounds ordinate is less than about 2000
pixels (although this may be dependent on the window height).
This bug has not been observed with other X servers. */
if ((tp[j].y += dy) < -canvas_height) tp[j].y = -canvas_height;
else if (tp[j].y > canvas_height) tp[j].y = canvas_height;
/* Convert all except the first ordinate in each set of contiguous
valid samples to relative ordinates. */
if (j < i-1 && tp[j+1].y != WFDB_INVALID_SAMPLE)
tp[j+1].y -= tp[j].y;
}
if (dc_coupled[c]) lp->sb[c] = sigbase[c]*vscale[c] + dy;
}
return (lp);
}
/* Clear_cache() marks all of the display lists in the cache as invalid. This
function should be executed whenever the gain (vscale) or record is changed,
or whenever the canvas width has been increased. */
void clear_cache()
{
int i;
struct display_list *lp;
if (canvas_width > vlist_size) {
for (lp = first_list; lp; lp = lp->next) {
for (i = 0; i < lp->nsig && lp->vlist[i] != NULL; i++) {
free(lp->vlist[i]);
lp->vlist[i] = NULL;
}
}
vlist_size = 0;
}
else {
for (lp = first_list; lp; lp = lp->next) {
lp->start = -1;
lp->npoints = 0;
}
}
}
static void show_signal_names()
{
int i, xoff, yoff;
xoff = mmx(5);
yoff = (nsig > 1) ? (base[1] - base[0])/3 : canvas_height/3;
if (sig_mode == 0)
for (i = 0; i < nsig; i++)
wave_draw_string(gtk_widget_get_window(wave_view),
draw_sig, xoff, base[i] - yoff,
signame[i], strlen(signame[i]));
else if (sig_mode == 1) {
for (i = 0; i < siglistlen; i++)
if (0 <= siglist[i] && siglist[i] < nsig)
wave_draw_string(gtk_widget_get_window(wave_view),
draw_sig, xoff, base[i] - yoff,
signame[siglist[i]], strlen(signame[siglist[i]]));
}
else { /* sig_mode == 2 (show valid signals only) */
int j, nvsig;
for (i = nvsig = 0; i < nsig; i++)
if (vvalid[i]) nvsig++;
for (i = j = 0; i < nsig; i++) {
if (vvalid[i]) {
base[i] = canvas_height*(2*(j++)+1.)/(2.*nvsig);
wave_draw_string(gtk_widget_get_window(wave_view),
draw_sig, xoff, base[i] - yoff,
signame[i], strlen(signame[i]));
}
}
}
}
static void show_signal_baselines(lp)
struct display_list *lp;
{
int i, l, xoff, yoff;
yoff = mmy(2);
for (i = 0; i < nsig; i++) {
if (base[i] == -9999) continue;
if (dc_coupled[i] && 0 <= lp->sb[i] && lp->sb[i] < canvas_height) {
gdk_draw_line(gtk_widget_get_window(wave_view), draw_ann,
0, lp->sb[i]+base[i], canvas_width, lp->sb[i]+base[i]);
if (blabel[i]) {
l = strlen(blabel[i]);
xoff = canvas_width - wave_text_width(blabel[i], l) - mmx(2);
wave_draw_string(gtk_widget_get_window(wave_view), draw_sig,
xoff, lp->sb[i]+base[i] - yoff, blabel[i], l);
}
}
}
}
/* Return window y-coordinate corresponding to the level of displayed trace
i at abscissa x. */
int sigy(i, x)
int i, x;
{
int ix, j = -1, xx, yy;
if (sig_mode != 1) j = i;
else if (0 <= i && i < siglistlen) j = siglist[i];
if (j < 0 || j >= nsig || lp_current->vlist[j] == NULL) return (-1);
if (nsamp > canvas_width) ix = x;
else ix = (int)x/tscale;
if (ix >= lp_current->ndpts) ix = lp_current->ndpts - 1;
for (xx = yy = 0; xx < ix; xx++)
yy += lp_current->vlist[j][xx].y;
return (yy + base[i]);
}