// // Tool to create stacked strip charts, or "zebragraphs" // // - useful for analysis of GPS 1PPS "sawtooth" jitter // // 15-May-2007 tvb // #include #include #include "bmp1_lib.h" char Usage[] = "Usage: zebra1 OUT.bmp stripes [yscale xscale width height] < IN.txt\n" "\n" "Examples:\n" " zebra1 jitter.bmp 4 1e2 < 1pps.txt\n" " zebra1 jitter.bmp 8 1e2 5 < 1pps.txt\n" " zebra1 1hour.bmp 6 1e2 1 600 600 < 1pps.txt\n" " zebra1 24hour.bmp 24 1e2 1 3600 2700 < 1pps.txt\n" " zebra1 1day.bmp 144 <1pps.txt 1e2 2 1200 8000\n" "\n" "Notes:\n" " - Input data should be detrended (0 slope) and normalized (0 mean).\n" " - Use the yscale parameter to scale input data to +/- 1.0 range.\n" " - Use xscale to spread data out horizontally, if desired.\n" " - Plot width and height default to 640x480 pixels.\n" ; int main (int argc, char *argv[]) { long data_count; char *filename; int stripes, stripe_index, stripe_height, stripe_half; int width, height; int x, y, oldx, oldy, y_center, y_major, xscale, j; double yscale, y_value; int get_data (double *); // Get parameters. if (argc < 3) { fprintf(stderr, Usage); exit(1); } filename = (argc > 1) ? argv[1] : "jitter.bmp"; stripes = (argc > 2) ? atoi(argv[2]) : 4; yscale = (argc > 3) ? atof(argv[3]) : 1.0; xscale = (argc > 4) ? atoi(argv[4]) : 1; width = (argc > 5) ? atoi(argv[5]) : 640; height = (argc > 6) ? atoi(argv[6]) : 480; // Silent sanity checks. if (stripes < 1) stripes = 1; if (xscale < 1) xscale = 1; if (width < 60) width = 60; if (height < 40) height = 40; // Split image into multiple stacked equally spaced horizontal stripes. bmp1_alloc(width, height); stripe_height = height / stripes; stripe_half = stripe_height / 2; y_major = stripe_height / 4; data_count = 0; stripe_index = 0; x = 0; while (get_data(&y_value) != 0) { data_count += 1; // Draw center, upper, and lower grid lines once each stripe. if (x == 0) { y_center = stripe_half + (stripe_height * stripe_index); for (j = -1; j <= 1; j += 1) { y = j * y_major; bmp1_lineto(0, y_center + y, width - 1, y_center + y); } } // Plot thick data point and draw thin line from previous point. y = (int) (-1 * y_major * y_value * yscale + 0.5); bmp1_square(x, y_center + y, 1); if (x > 0) { bmp1_lineto(oldx, y_center + oldy, x, y_center + y); } oldx = x; oldy = y; // Advance to next point, or wrap around and down to next stripe. x += xscale; if (x > width - 1) { x = 0; stripe_index += 1; if (stripe_index == stripes) { break; } } } // Warn if any points were out of range. if (bmp1_clip_count() != 0) { fprintf(stderr, "WARNING: %ld points clipped; y scale too large?\n", bmp1_clip_count()); } // Write finished bitmap to output file. y = (stripe_index == stripes) ? height : (y_center + stripe_half); bmp1_write(filename, y); fprintf(stderr, " data: %ld points read.\n", data_count); fprintf(stderr, "stripes: %d requested, %.2lf completed.\n", stripes, stripe_index + (double)x / width); fprintf(stderr, " height: %d requested, %d needed.\n", height, y); return 0; } // // Get raw data; after scaling it should range from -1 min to +1 max. // int get_data (double *value) { char line[100]; while (fgets(line, sizeof line, stdin) != NULL) { if (line[0] != '#') { if (1 != sscanf(line, "%lf", value)) { fprintf(stderr, "Non-numeric input data: `%s'\n", line); exit(1); } return 1; } } return 0; } // Poor man's makefile... #include "bmp1_lib.c"