Set the cut path properly when a non-default output path is specified
[atscap.git] / xsig.c
blob3f2e6e9a267852fbcb0fcd0f43dbe9163e952375
1 #define NAME "xsig"
2 #define AUTHOR "inkling"
3 #define EMAIL "inkling@nop.org"
4 #define WEBPAGE "http://www.nop.org/inkling/dtv"
5 #define COPYRIGHT "Copyright (C) 2004-2007"
6 #define LICENSE "GNU General Public License"
7 #define LASTEDIT "20071007"
8 #define VERSION "1.4"
9 #warning ignore any OpenGL in this file.
11 * xsig.c (c) Copyright 2004-2005 by inkling@nop.org
12 * V4L/V4L2/DVB API Signal Strength Display
14 * xsig is free software; you may only redistribute it and/or modify
15 * it under the terms of the GNU General Public License, Version 2,
16 * as published by the Free Software Foundation.
18 * xsig is distributed to you in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write the Free Software Foundation, Inc.,
25 * at 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 /* Peter Knaggs Peter.Knaggs@gmail.com submitted code for DVB API 2005-Sep-7 */
31 WHAT IT DOES
33 This program reads V4L, V4L2 or DVB API signal strength
34 and displays it as a running graph of signal versus time.
36 By default, it will try to draw, in X, a polar chart, where
37 latitude is signal strength and longitude is time, clockwise.
39 Optional display in text mode is available with -t, but it only
40 works with console or xterm, not aterm because of scrolling region.
42 *******************************************************************************
43 ************ NOTE: EDIT THE TABLE atsc_bcast[] BELOW FOR YOUR AREA ************
44 *******************************************************************************
50 EXAMPLE USAGE:
52 sig -i0 19
54 Will set device /dev/dtv0 to UHF channel 19 and begin signal scan
56 sig -a -i0 19
58 Will set device /dev/dtv0 to UHF channel 19 and begin signal scan,
59 with variable audio pitch feedback for signal strength.
63 CHANGELOG
65 sig original experiment to get HD2000 LED status in software
67 0.1 first version based on dtvsignal from JSK, driver 1.4
68 0.2 use LEDs from GPIO on HD2000 with custom driver 1.4
70 atscsig pcHDTV ATSC drivers only
72 0.3 use V4L2 with stock driver 1.6 and above
73 0.4 chg switched to signal bar like atscap, scrapped LEDs
74 0.5 add set scrolling region, xterm | console OK, aterm broken
75 0.6 add audio indication of signal strength. bad sigs odd melodies
76 0.7 add X histogram for signal strength. drab and plain.
77 0.8 chg to polar plot power colors vs time circle, eye candy
78 0.9 add openGL support for polar plot (work in progress)
79 1.0 chg to using V4L2 or V4L1 interface for driver 2.0 or 1.6
82 xsig name is now xsig to reflect support for V4L / V4L2 / DVB API
84 1.1 add DVB interface, -1 -2 -3 options set video API
85 1.2 chg reduce DVB API ioctls, fix -t mode, msgs, add % to polar
86 1.3 chg for backwards compatibility test on V4L1 with 2.4.26
87 1.4 add DVB FE_TUNE_MODE_ONESHOT ioctl for i2c collision avoidance
89 /* NOTES:
91 Ignore all the Open GL code. I'll flesh it out someday.
94 /* BUGS:
95 died on 2.4.26 with V4L driver 1.6: floating point exception, but
96 works ok with DVB API. I can't support that old V4L code anymore.
97 */
99 #include <stdio.h>
100 #include <stdlib.h>
101 #include <unistd.h>
102 #include <sys/types.h>
103 #include <sys/stat.h>
104 #include <fcntl.h>
105 #include <string.h>
106 #include <errno.h>
107 #include <getopt.h>
108 #include <sys/ioctl.h>
109 #include <inttypes.h>
110 #include <time.h>
111 #include <math.h>
113 #include <X11/Xlib.h>
114 #include <X11/Xutil.h>
115 #include <X11/cursorfont.h>
117 #include <sys/soundcard.h>
119 #if USE_GL
120 #include <GL/gl.h>
121 #include <GL/glu.h>
122 #include <GL/glut.h>
123 #endif
125 #ifdef USE_DVB
126 #include <linux/dvb/frontend.h>
127 #include <linux/dvb/dmx.h>
128 #endif
130 /* V4L is deprecated as of june 2006, do not use */
131 #ifdef USE_V4L
132 #warning obsolete V4L API via -1 -2 options
133 #include <linux/videodev.h>
134 #include <linux/videodev2.h>
135 #endif
137 #define WHO fprintf(stderr, "%s:\n", __FUNCTION__)
140 #define xprintf if (0!=arg_xmsg) fprintf
142 static int arg_once = 0; // exit after first loop, give scans
143 static int arg_adsp = 0; // audio enable
144 static int arg_athr = 0; // audio threshold
145 static int arg_xdim = 0; // X dimensions, height/width are same
146 static int arg_xmsg = 0; // NZ show X debug messages
147 static int arg_otxt = 0; // NZ text output only
148 static int arg_ox11 = ~0; // NZ X output only
149 static int arg_idev = 0; // dtv0-3
150 static int arg_loop = 1; // 1 360 loop by default cuz dvb so slow
151 static int arg_modu = 3; // loop modulus, get sig once every modulus
152 static int arg_time = 0; // sleep time in nanos
153 static int arg_wipe = 0; // wipe display after each loop
155 #ifdef USE_DVB
156 static int arg_vapi = 3; // 1 = V4L1 2 = V4L2 3 = DVB
157 #endif
159 #ifdef USE_V4L
160 static int arg_vapi = 1; // 1 = V4L1 2 = V4L2 3 = DVB
161 #endif
163 /******************************************************** X related globals */
164 static Display *mydisplay;
165 static Window mywindow;
166 static XWindowAttributes attribs;
167 static XEvent xev;
168 static XSetWindowAttributes xswa;
169 static int myscreen; /* X screen from init */
170 static int xinit = 0; /* X window initialized */
171 static int xbpp = 0; /* X bits per pixel depth */
172 static int xtbh = 0; /* X title bar height */
173 static int xmaxi = 0; /* NOTE: X window not maximized by default */
174 static int xlbt0 = 0; /* X left button time 0 */
175 static int xlbtd = 0; /* X left button time delta */
176 static int xrszlock = 0; /* don't interrupt x resize */
177 static unsigned int fg, bg; /* x foreground and background colors */
178 static GC mygc;
179 static XGCValues xgcv;
180 static XImage *myximage;
181 static unsigned char *ximagedata;
182 static XFontStruct * myxfonts;
183 static unsigned char xtitle[256];
184 static unsigned char xtext[256];
185 static XSizeHints hint;
187 unsigned int *xcolors;
189 unsigned int xcolors565[] = {
190 (0x07 << 11) | (0x0F << 6) | 0x07, // lgrey
191 (0x00 << 11) | (0x00 << 6) | 0x1F, // B
192 (0x1F << 11) | (0x00 << 6) | 0x1F, // M
193 (0x1F << 11) | (0x00 << 6) | 0x00, // R
194 (0x1F << 11) | (0x3F << 6) | 0x00, // Y
195 (0x00 << 11) | (0x3F << 6) | 0x00, // G
196 (0x00 << 11) | (0x3F << 6) | 0x1F, // C
197 (0x1F << 11) | (0x3F << 6) | 0x1F, // W
198 (0x1F << 11) | (0x3F << 6) | 0x1F, // W
199 (0x03 << 11) | (0x03 << 6) | 0x03, // dgrey
202 unsigned int xcolors888[] = {
203 (0x40 << 16) | (0x40 << 8) | 0x40, // lgrey
204 (0x00 << 16) | (0x00 << 8) | 0xFF, // B
205 (0xFF << 16) | (0x00 << 8) | 0xFF, // M
206 (0xFF << 16) | (0x00 << 8) | 0x00, // R
207 (0xFF << 16) | (0xFF << 8) | 0x00, // Y
208 (0x00 << 16) | (0xFF << 8) | 0x00, // G
209 (0x00 << 16) | (0xFF << 8) | 0xFF, // C
210 (0xFF << 16) | (0xFF << 8) | 0xFF, // W
211 (0xFF << 16) | (0xFF << 8) | 0xFF, // W
212 (0x10 << 16) | (0x10 << 8) | 0x10, // dgrey
215 struct vw_s {
216 int x;
217 int y;
218 int width;
219 int height;
222 static struct vw_s vw = { 512, 0, 314, 314 }; // initpos, x changed so far
223 static struct vw_s vw0 = { 0, 0, 0, 0 }; // max/min toggle
225 double fstrength = 0.0;
226 double fdegrees = 0.0;
227 double fxscale, fyscale;
228 int xorigin, yorigin, xend, yend;
230 double pi = 3.1415926;
231 double pi2 = 6.2831852;
233 /********************************************************* v4l/tuner globals */
236 static int in_file;
238 static unsigned char in_name[256];
239 static unsigned char in_sname[8];
241 static int chan = 2;
242 struct chan_s {
243 char *sid;
244 int freq;
248 static int strength = 0; // signal strength
249 static unsigned long freq = 1; // current frequency
250 static unsigned long pfreq = 0; // previous frequency
251 static int sig1 = 0, sig2 = 0, siglock = 0, sigper = 0;
252 static int ledG = 0, ledR = 0;
255 unsigned int use_bar = ~0;
258 #ifdef USE_DVB
259 // dvb uses to tune to a channel (frequency)
260 struct dvb_frontend_parameters dvb_frontend_param;
261 #endif
264 #ifdef USE_V4L
265 static struct video_signal vsig;
266 static struct v4l2_tuner v2sig;
268 struct video_channel vch_ATSC = {
270 "chan --",
272 VIDEO_VC_TUNER,
273 VIDEO_TYPE_TV,
274 VIDEO_MODE_ATSC
277 // DELETEME: why is this here? leftover?
278 struct video_channel vch_NTSC = {
282 VIDEO_VC_TUNER,
283 VIDEO_TYPE_TV,
284 VIDEO_MODE_NTSC
286 #endif
288 // count number of times sig scan actually done
290 // jostle the scheduler
291 struct timespec tune_sleep = { 0, 250000000 }; // quarter second for tuner
292 struct timespec sig_sleep = { 0, 25000 }; // 25 microseconds
293 struct timespec loop_start = { 0, 0 }; // time spent in get sig
294 struct timespec loop_stop = { 0, 0 };
295 struct timespec loop_diff = { 0, 0 };
297 /* NOTE: you need to edit these for your area */
298 static struct chan_s atsc_bcast[] = {
299 { " 0", 43250 },
300 { " 1", 49250 },
301 { " 2", 55250 },
302 { " 3", 61250 },
303 { " 4", 67250 },
304 { " 5", 77250 },
305 { " 6", 83250 },
307 { " 7", 175250 },
308 { " 8", 181250 },
309 { " 9", 187250 },
310 { "10", 193250 },
311 { "11", 199250 },
312 { "12", 205250 },
313 { "13", 211250 },
315 { "14", 471250 },
316 { "15", 477250 },
317 { "16", 483250 },
318 { "17", 489250 },
319 { "18", 495250 },
320 { "19", 501250 },
321 { "20", 507250 },
322 { "21", 513250 },
323 { "22", 519250 },
324 { "23", 525250 },
325 { "24", 531250 },
326 { "25", 537250 },
327 { "26", 543250 },
328 { "27", 549250 },
329 { "28", 555250 },
330 { "29", 561250 },
331 { "30", 567250 },
332 { "31", 573250 },
333 { "32", 579250 },
334 { "33", 585250 },
335 { "34", 591250 },
336 { "35", 597250 },
337 { "36", 603250 },
338 { "37", 609250 },
339 { "38", 615250 },
340 { "39", 621250 },
341 { "40", 627250 },
342 { "41", 633250 },
343 { "42", 639250 },
344 { "43", 645250 },
345 { "44", 651250 },
346 { "45", 657250 },
347 { "46", 663250 },
348 { "47", 669250 },
349 { "48", 675250 },
350 { "49", 681250 },
351 { "50", 687250 },
352 { "51", 693250 },
353 { "52", 699250 },
354 { "53", 705250 },
355 { "54", 711250 },
356 { "55", 717250 },
357 { "56", 723250 },
358 { "57", 729250 },
359 { "58", 735250 },
360 { "59", 741250 },
361 { "60", 747250 },
362 { "61", 753250 },
363 { "62", 759250 },
365 // not supposed to have any stations up here but let user try for themselves
366 { "63", 765250 },
367 { "64", 771250 },
368 { "65", 777250 },
369 { "66", 783250 },
370 { "67", 789250 },
371 { "68", 795250 },
372 { "69", 801250 },
374 { "70", 807250 },
375 { "71", 813250 },
376 { "72", 819250 },
377 { "73", 825250 },
378 { "74", 831250 },
379 { "75", 837250 },
380 { "76", 843250 },
381 { "77", 849250 },
382 { "78", 855250 },
383 { "79", 861250 },
384 { "80", 867250 },
385 { "81", 873250 },
386 { "82", 879250 },
387 { "83", 885250 },
390 static int scan_list[82];
391 static int scan_count = 0;
393 static int strength_count = 0;
394 static int strength_sum = 0;
395 static int strength_avg = 0;
398 #define OSS_DEVICE "/dev/dsp0"
399 #define OSS_BUF_SIZE 4096
400 #define SAMPLE_RATE 44100
401 #define SAMPLE_CHANNELS 1
402 #define SAMPLE_BYTES 2
403 // 1/16th of a second
404 #define SAMPLE_SIZE ((SAMPLE_RATE * SAMPLE_CHANNELS) >> 4)
405 // 1 second
406 //#define SAMPLE_SIZE (SAMPLE_RATE * SAMPLE_CHANNELS)
408 static int audio_fd;
409 //unsigned char audio_buf[OSS_BUF_SIZE];
410 static signed short audio_sample[ SAMPLE_SIZE ];
411 static int sample_index = 0;
413 #define CHROMATIC_SCALE 12
414 static int chromatic_scale[] =
416 // n * 2 to the power of 1/12, 101 entries for 0-100% (practical 22-84?)
417 // A A#/Bb B C C#/Db D D#/Eb E F F#/Gb G G#/Ab
418 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104,
419 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208,
420 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415,
421 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831,
422 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661,
423 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322,
424 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645,
425 7040, 7459, 7902, 8372, 8870, 9397, 9956,10548,11175,11840,12544,13290,
426 14080,14917,15804,16744,17740
429 // based on above
430 static int diatonic_scale[] =
432 // A A# B C C# D D# E F F# G G#
433 // -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0
434 0, 2, 3, 5, 7, 8, 10,
435 12, 14, 15, 17, 19, 20, 22,
436 24, 26, 27, 29, 31, 32, 34,
439 /************************************************************** BEGIN GLOBALS */
441 int fullscreen = 0;
444 #undef USE_GL
445 #ifdef USE_GL
446 struct mouse_s {
447 int called;
448 int button;
449 int updown;
450 int x;
451 int y;
453 static struct mouse_s mouse;
455 static struct {
456 int w;
457 int h;
458 int x;
459 int y;
460 } origin;
461 static struct timespec gl_sleep = { 0, 5000000 }; // 5 mS
463 // define a point in space as x,y,z and set the color value for it
464 struct point3f_s {
465 GLfloat x;
466 GLfloat y;
467 GLfloat z;
468 GLfloat r;
469 GLfloat g;
470 GLfloat b;
472 //static struct point3f_s point;
474 GLfloat spin = 0.0;
475 GLuint theGrid;
477 /*************************************************************** PROTOTYPES */
478 void glutCloseFunc( void * );
479 void glutLeaveMainLoop( void );
480 #endif
482 static void c_exit( int ev );
484 /******************************************************************* TIMERS */
485 // put timespec nanosecond time difference from z - y into x
486 static
487 void
488 time_diff( struct timespec *x, struct timespec *y, struct timespec *z)
490 time_t sec;
491 long nsec;
493 sec = z->tv_sec;
494 nsec = z->tv_nsec;
496 if (y->tv_nsec > nsec) { // borrow implicit second if/when clock wraps
497 nsec += 1000000000;
498 sec--;
500 x->tv_nsec = nsec - y->tv_nsec;
501 x->tv_sec = sec - y->tv_sec;
505 #ifdef USE_GL
506 /******************************************************** GL/glut FUNCTIONS */
508 static
509 void
510 GLGrid( void )
512 theGrid = glGenLists( 1 );
513 glNewList( theGrid, GL_COMPILE );
515 glColor3f ( 0.0, 1.0, 0.0 );
516 glPushMatrix();
517 glBegin( GL_LINES );
518 glVertex3f( -1.0, -1.0, 0.0 );
519 glVertex3f( 1.0, 1.0, 0.0 );
520 glEnd();
521 glPopMatrix();
523 glColor3f ( 1.0, 0.0, 0.0 );
524 glPushMatrix();
525 glBegin( GL_LINES );
526 glVertex3f( 1.0, -1.0, 0.0 );
527 glVertex3f( -1.0, 1.0, 0.0 );
528 glEnd();
529 glPopMatrix();
531 glColor3f ( 1.0, 1.0, 0.0 );
532 glPushMatrix();
533 glBegin( GL_LINES );
534 glVertex3f( 0.0, 1.0, 0.0 );
535 glVertex3f( 0.0, -1.0, 0.0 );
536 glEnd();
537 glPopMatrix();
539 glColor3f ( 1.0, 0.0, 1.0 );
540 glPushMatrix();
541 glBegin( GL_LINES );
542 glVertex3f( 1.0, 0.0, 0.0 );
543 glVertex3f( -1.0, 0.0, 0.0 );
544 glEnd();
545 glPopMatrix();
547 glEndList();
551 /* initialize rgba mode with antialias, alpha blend, hint, and line width */
552 static
553 void
554 GLInit( void )
556 // if you need to know granularity and max line width
557 // GLfloat v[2];
558 // glGetFloatv( GL_LINE_WIDTH_GRANULARITY, v);
559 // glGetFloatv( GL_LINE_WIDTH_RANGE, v);
561 GLGrid();
563 glEnable( GL_LINE_SMOOTH ); // anti-alias
565 glEnable( GL_BLEND ); // alpha blend
566 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // alpha blend
568 glHint( GL_LINE_SMOOTH_HINT, GL_DONT_CARE ); // hint is dont care?
570 glLineWidth( 1.0 ); // line width
571 glClearColor ( 0.0, 0.0, 0.0, 0.0 ); // background pixel color
574 /*///////////////////////////////////////////////////////////////////////////*/
576 static
577 void
578 GLDisplay( void )
580 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
582 glPushMatrix(); // viewpoint matrix push
583 glTranslatef ( 0.0, 0.0, -5.0 ); // viewpoint translate
585 glRotatef( spin, 1.0, 1.0, 1.0 );
587 glCallList( theGrid );
589 glPopMatrix(); // viewpoint pop
590 // glFlush();
591 glutSwapBuffers();
592 // nanosleep( &gl_sleep, NULL );
596 static
597 void
598 GLReshape(int w, int h)
600 glViewport(0, 0, w, h);
601 glMatrixMode( GL_PROJECTION );
602 glLoadIdentity();
603 gluPerspective( 20.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
604 glMatrixMode( GL_MODELVIEW );
608 static
609 void
610 GLLeftMouseButton( void )
612 fprintf( stdout, "Left Mouse Down\n");
615 static
616 void
617 GLMiddleMouseButton( void )
619 fullscreen = ~fullscreen;
620 fprintf( stdout, "Middle Mouse Down -- ");
621 fprintf( stdout, "Fullscreen %s\n", (0==fullscreen)?"NO":"YES");
622 if (0 != fullscreen) {
623 glutFullScreen();
624 } else {
625 GLReshape( origin.w, origin.h );
629 static
630 void
631 GLRightMouseButton( void )
633 fprintf( stdout, "Right Mouse Down\n");
636 static
637 void
638 GLMouse( int b, int u, int x, int y)
640 GLint w;
641 w = glutGetWindow();
642 mouse.called = 1;
643 mouse.button = b;
644 mouse.updown = u;
645 mouse.x = x;
646 mouse.y = y;
647 fprintf( stdout, "Mouse: w %d b %d u %d x %d y %d\n", w, b, u, x, y );
648 if ( (0 == b) && (0 == u) ) GLLeftMouseButton();
649 if ( (1 == b) && (0 == u) ) GLMiddleMouseButton();
650 if ( (2 == b) && (0 == u) ) GLRightMouseButton();
654 static
655 void
656 GLKeyboard( unsigned char c, int i, int j )
658 fprintf( stdout, "Keyboard: c '%c' i %d j %d\n", c, i, j );
660 if ( 'q' == c )
661 glutLeaveMainLoop();
663 if ( 'f' == c ) {
664 fullscreen = ~fullscreen;
665 if (0 != fullscreen) {
666 glutFullScreen();
667 } else {
668 GLReshape( origin.w, origin.h );
675 static
676 void
677 GLClose( void )
679 GLint w;
680 w = glutGetWindow();
681 fprintf( stdout, "Close: w %d\n", w);
682 glutLeaveMainLoop();
686 // this function gets called when not doing anything.
687 // could make it change the location of the line
688 static
689 void
690 GLIdle( void )
692 // change parameters for the line
693 spin += 1.0;
694 // ...
695 /// and display then sleep
696 GLDisplay(); // display needed here? think so
697 // nanosleep( &gl_sleep, NULL );
701 /************************************************************ END FUNCTIONS */
704 /****************************************************************** GL MAIN */
706 gl_main(int argc, char** argv)
708 int glut_window;
710 origin.w = origin.h = 300;
711 origin.x = origin.y = 0;
713 glutInitWindowSize( origin.w, origin.h );
714 glutInitWindowPosition( origin.x, origin.y );
716 glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
717 glutInit( &argc, argv );
719 glut_window = glutCreateWindow( "Polar Plot: Strength vs. Time");
720 GLInit();
722 // hook in various glut event callbacks
723 glutReshapeFunc( GLReshape );
724 glutKeyboardFunc( GLKeyboard );
725 glutMouseFunc( GLMouse );
726 glutCloseFunc( GLClose );
727 glutIdleFunc( GLIdle );
728 glutDisplayFunc( GLDisplay );
730 glutMainLoop( ); // waits until closed or [q]uit key hit
732 return 0;
735 #endif /* USE_GL */
738 /********************************************************************* AUDIO */
739 static
740 void
741 open_audio( void )
743 WHO;
744 audio_fd = open( OSS_DEVICE, O_WRONLY, 0 );
745 if (audio_fd == -1) {
746 perror( "OPEN ERROR " OSS_DEVICE );
747 exit(1);
749 fprintf(stderr, "Audio open\n");
752 static
753 void
754 close_audio( void )
756 int ok;
757 WHO;
758 ok = close(audio_fd);
759 if (ok == -1) {
760 perror( "CLOSE ERROR " OSS_DEVICE );
761 exit(1);
763 fprintf(stderr, "Audio closed\n");
766 // build a tone in audio sample buffer
767 // if clear is 0, values from new tone added to old tone
768 static
769 void
770 build_frequency( int afreq, int clear )
772 double pi1 = 3.1415926;
773 double sample_scale;
774 double samples_cycle;
775 int i, j;
777 if (0 != clear)
778 memset( audio_sample, 0, sizeof(audio_sample) );
780 samples_cycle = SAMPLE_RATE / afreq;
781 sample_scale = (2 * pi1) / samples_cycle;
782 // fprintf( stdout, "Samples per cycle %f scale %f\n", samples_cycle, sample_scale);
784 // i bumped below
785 for (i = 0; i < (sizeof(audio_sample)>>1); ) {
786 for (j = 0; j < samples_cycle; j++) {
787 sample_index = i + j; // current sample
788 // want -32768 to +32767 and 0
789 if ( sample_index < (sizeof(audio_sample)>>1) )
791 int k;
792 k = 0x8001;
793 // new values
794 if (0 != clear) {
795 k = 32767 * sin( j * sample_scale );
796 } else {
797 k = audio_sample[ sample_index ];
798 // try additive for now but probably wrong
799 // k += (32767 * sin( j * sample_scale ));
802 // clamp
803 if (k < -32767) k = -32767;
804 // clamp
805 if (k > 32767) k = 32767;
807 audio_sample[ sample_index ] = k;
809 // fprintf( stdout, "sample %d value %d\n", i+j, audio_sample[i+j] );
812 i+= j;
817 // use normal table index to build semitone n (0-11 for first octave)
818 static
819 void
820 build_semitone( int n )
822 build_frequency( chromatic_scale[ n ], 1 );
825 // use diatonic table index to build full tone n (0-7 for first octave)
826 static
827 void
828 build_tone( int n )
830 build_frequency( chromatic_scale[ diatonic_scale [ n ] ], 1);
834 // set audio device parameters: Signed 16 bit Any Endian at 44100 Hz monaural
835 // device should be open prior to call
836 static
837 void
838 setup_audio( void )
840 int format, channels, rate, ir;
841 format = AFMT_S16_NE;
842 channels = SAMPLE_CHANNELS;
843 rate = SAMPLE_RATE;
845 WHO;
846 ir = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
847 if (ir == -1) {
848 perror( "SNDCTL_DSP_SETFMT ERROR " OSS_DEVICE );
849 exit(1);
851 fprintf( stderr, "%s SNDCTL_DSP_SETFMT\n", OSS_DEVICE);
853 if (AFMT_S16_LE != format) {
854 fprintf( stderr, "SNDCTL_DSP_SETFMT NOT S16LE %s\n", OSS_DEVICE );
855 exit(1);
857 fprintf( stderr, "%s SNDCTL_DSP_SETFMT AFMT_S16_LE\n", OSS_DEVICE);
859 ir = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels);
860 if (ir == -1) {
861 perror( "SNDCTL_DSP_CHANNELS ERROR " OSS_DEVICE );
862 exit(1);
864 fprintf( stderr, "%s SNDCTL_DSP_CHANNELS\n", OSS_DEVICE);
866 if (1 != channels) {
867 fprintf( stderr, "SNDCTL_DSP_CHANNELS NO MONO %s\n", OSS_DEVICE);
868 exit(1);
870 fprintf( stderr, "%s SNDCTL_DSP_CHANNELS has monaural\n", OSS_DEVICE);
872 ir = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
873 if (ir == -1) {
874 perror( "SNDCTL_DSP_SPEED ERROR " OSS_DEVICE );
875 exit(1);
877 fprintf( stderr, "%s SNDCTL_DSP_SPEED %d\n", OSS_DEVICE, rate);
879 // ignore the rate being wrong, unpatched es1371 drive reports 44101
880 // if (44100 != rate) {
881 // fprintf( stderr, "SNDCTL_DSP_SPEED %d\n", rate);
882 // }
884 build_tone( 12 ); // should be 110Hz A, 0-11 is inaudible for some
887 // send tone to device
888 static
889 void
890 play_audio( int duration ) {
891 int len;
892 int samp_len;
894 #if 0
895 samp_len = (sample_index * duration)/100;
896 if (samp_len < 0) samp_len = 0;
897 if (samp_len > sample_index) samp_len = sample_index;
898 // samp_len = sample_index - samp_len;
900 // fprintf( stderr, "sample index %d duration %d samplen %d\n",
901 // sample_index, duration, samp_len);
902 #endif
903 samp_len = sample_index;
905 // most likely will fail and have to check len for how much written
906 if (samp_len != 0) {
907 len = write( audio_fd, audio_sample, samp_len );
908 if (len == -1) {
909 perror( "AUDIO WRITE " OSS_DEVICE);
910 exit(1);
913 // fprintf( stderr, "Wrote %d bytes\n", len);
918 static void init_audio( void ) {
919 open_audio();
920 setup_audio();
924 /**************************************************************** X functions */
926 /* compute vertical x offset from fontsize and text line (row) number */
927 /* myxfonts is what font was loaded */
928 /* FIXME: doesn't check for offscreen */
929 static
931 x_fontline( int row )
933 int ytot;
935 // if (0 == xinit) return;
937 /* total font height = cell height (ascender) + descender + blank */
938 ytot = myxfonts->ascent + myxfonts->descent + 1; /* 1 or 2 blanks? */
940 /* however, font baseline does not count descender (leave a blank?) */
941 /* should already have ytot correct with ascent+descent+blank */
942 return (row * ytot) + myxfonts->ascent + 1;
943 // return (row * ytot);
946 static
947 void
948 x_title( void )
950 if (0 == xinit) return;
952 /* tell window manager about this window: title, icon, hw, xy */
953 /* XSetWMProperties is more hassle than is needed here */
954 XSetStandardProperties( mydisplay,
955 mywindow,
956 xtitle,
957 xtitle,
958 None,
959 NULL,
961 &hint
966 static
967 void
968 x_draw_filled_rectangle( int c, int x, int y, unsigned int w, unsigned int h)
970 if (0 == xinit) return;
972 XSetForeground( mydisplay, mygc, c );
973 // XDrawRectangle( mydisplay, mywindow, mygc, x,y,w,h); // not needed
974 XFillRectangle( mydisplay, mywindow, mygc, x,y,w,h);
977 static
978 void
979 x_display_grid( void )
981 int c, j, x, y, h, w;
982 int xoffset = 0;
983 int yoffset = 0;
985 if (0 == xinit) return;
987 x = vw.width/2;
988 y = vw.height/2;
989 h = vw.width;
990 w = vw.height;
992 #if 0
993 // crosshairs then circles
994 XSetForeground( mydisplay, mygc, 0xFFFF);
995 XDrawLine( mydisplay, mywindow, mygc, 0, 0, vw.width-1,vw.height-1);
996 XDrawLine( mydisplay, mywindow, mygc, 0, vw.height-1,vw.width-1,0);
997 XDrawLine( mydisplay, mywindow, mygc, vw.width/2, 0, vw.width/2, vw.height-1);
998 XDrawLine( mydisplay, mywindow, mygc, 0, vw.height/2, vw.width-1, vw.height/2);
999 XFlush( mydisplay );
1000 #endif
1002 #define STEP 12
1003 for (j=0; j < 100;) {
1004 c = xcolors[j / STEP];
1005 XSetForeground( mydisplay, mygc, c );
1007 XDrawArc( mydisplay, mywindow, mygc,
1008 x - ((j/100.0)*(w / 2)), y - ((j/100.0)*(h / 2)),
1009 (h * j) / 100.0, (w *j) / 100.0, 0, 360 * 64
1011 j += STEP;
1013 XFlush( mydisplay );
1015 // show signal percentage too
1017 memset( xtext, 0 , sizeof(xtext) );
1018 snprintf( xtext, sizeof(xtext)-1, "%3d%%", strength );
1019 xoffset = vw.width - 46;
1020 yoffset = x_fontline(1);
1022 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1023 XDrawImageString( mydisplay,
1024 mywindow,
1025 mygc,
1026 xoffset,
1027 yoffset,
1028 xtext,
1029 strlen(xtext)
1033 XFlush( mydisplay );
1039 // shows which channel is being charted
1040 static
1041 void
1042 x_display_channel( void )
1044 int xoffset = 0;
1045 int yoffset = 0;
1046 unsigned char *sid;
1048 if (0 == xinit) return;
1050 sid = atsc_bcast[chan].sid;
1052 /* create string, e.g. 19 KTXH UPN */
1053 memset( xtext, 0, sizeof(xtext));
1054 snprintf( xtext, sizeof(xtext)-1, "%-11s", sid );
1057 /* top left corner, second font line */
1058 xoffset = 0;
1059 yoffset = x_fontline(0);
1061 XSetBackground( mydisplay, mygc, bg );
1062 fg = xcolors[7];
1063 XSetForeground( mydisplay, mygc, fg );
1065 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1066 XDrawImageString( mydisplay,
1067 mywindow,
1068 mygc,
1069 xoffset,
1070 yoffset,
1071 xtext,
1072 strlen(xtext)
1075 XFlush( mydisplay );
1077 memset( xtext, 0 , sizeof(xtext) );
1078 snprintf( xtext, sizeof(xtext)-1, "%s", in_sname );
1079 // xoffset = 0;
1080 // yoffset = x_fontline(1);
1081 xoffset = vw.width - 46;
1082 yoffset = x_fontline(0);
1084 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1085 XDrawImageString( mydisplay,
1086 mywindow,
1087 mygc,
1088 xoffset,
1089 yoffset,
1090 xtext,
1091 strlen(xtext)
1095 XFlush( mydisplay );
1099 // the rest belongs in display grid, except it flickers there
1100 memset( xtext, 0 , sizeof(xtext));
1101 snprintf( xtext, sizeof(xtext)-1, "Bad");
1102 xoffset = 0;
1103 yoffset = vw.height - 2;
1105 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1106 XSetForeground( mydisplay, mygc, fg );
1107 XDrawImageString( mydisplay,
1108 mywindow,
1109 mygc,
1110 xoffset,
1111 yoffset,
1112 xtext,
1113 strlen(xtext)
1115 // draw rectangles for bad color
1116 yoffset -= x_fontline(0)+9;
1117 // xoffset += 44;
1118 x_draw_filled_rectangle( xcolors[0], xoffset, yoffset, 10, 10 );
1119 xoffset += 11;
1120 x_draw_filled_rectangle( xcolors[1], xoffset, yoffset, 10, 10 );
1121 xoffset += 11;
1122 x_draw_filled_rectangle( xcolors[2], xoffset, yoffset, 10, 10 );
1123 xoffset += 11;
1124 x_draw_filled_rectangle( xcolors[3], xoffset, yoffset, 10, 10 );
1125 XFlush( mydisplay );
1127 memset( xtext, 0 , sizeof(xtext));
1128 snprintf( xtext, sizeof(xtext)-1, "Good");
1129 xoffset = vw.width - 46;
1130 yoffset = vw.height - 2;
1132 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1133 XSetForeground( mydisplay, mygc, fg );
1134 XDrawImageString( mydisplay,
1135 mywindow,
1136 mygc,
1137 xoffset,
1138 yoffset,
1139 xtext,
1140 strlen(xtext)
1143 // draw rectangles for good color
1144 yoffset -= x_fontline(0)+9;
1145 // xoffset -= 55;
1146 x_draw_filled_rectangle( xcolors[4], xoffset, yoffset, 10, 10 );
1147 xoffset += 11;
1148 x_draw_filled_rectangle( xcolors[5], xoffset, yoffset, 10, 10 );
1149 xoffset += 11;
1150 x_draw_filled_rectangle( xcolors[6], xoffset, yoffset, 10, 10 );
1151 xoffset += 11;
1152 x_draw_filled_rectangle( xcolors[7], xoffset, yoffset, 10, 10 );
1154 XFlush( mydisplay );
1158 static
1159 void
1160 x_clear_display( void )
1162 if (0 == xinit) return;
1164 XClearWindow( mydisplay, mywindow );
1165 XFlush( mydisplay );
1169 static
1170 void
1171 x_redraw_display( int clear )
1173 if (0 == xinit) return;
1175 if (0 != clear) x_clear_display();
1176 x_display_channel(); // new channel gets new title
1177 x_display_grid();
1181 /***********************************************************************
1182 * start of main block of X window support functions
1183 * FIXME: all of these functions need some kind of error handling
1184 ***********************************************************************/
1186 static void
1187 x_resize_display( void )
1189 // int aspect = 0;
1190 XSetWindowAttributes nxswa;
1191 unsigned long nxswamask;
1192 Cursor fscur;
1193 XColor fgnd;
1194 XColor bgnd;
1196 if (0 == xinit) return;
1198 nxswamask = CWOverrideRedirect;
1200 if (xrszlock != 0) return; /* do nothing if in middle of resize */
1202 xrszlock = 1; /* lock against click happy */
1204 if (0 != xmaxi)
1206 /* unmap the window so we can change attributes */
1207 /* loses focus? unmap sent back to previous window? */
1208 XUnmapWindow( mydisplay, mywindow );
1209 /* wait and eat notify events until we get UnmapNotify */
1212 } while ( False == XCheckTypedWindowEvent( mydisplay,
1213 mywindow,
1214 UnmapNotify,
1215 &xev
1219 /* turn off window manager dressing */
1220 nxswa.override_redirect = True;
1221 XChangeWindowAttributes( mydisplay,
1222 mywindow,
1223 nxswamask,
1224 &nxswa
1227 /* fullscreen cursor has fg/bg same color (colorkey) */
1228 fscur = XCreateFontCursor( mydisplay, XC_crosshair );
1230 /* now color and define the fullscreen cursor */
1231 /* fixme, needs depth check and r<< g<< b<< appropriate */
1232 fgnd.pixel = 1;
1233 fgnd.red = 65535;
1234 fgnd.blue = 65535;
1235 fgnd.green = 65535;
1236 fgnd.flags = DoRed | DoGreen | DoBlue;
1238 bgnd.pixel = 0;
1239 bgnd.red = bgnd.blue = bgnd.green = 0;
1240 bgnd.flags = DoRed | DoGreen | DoBlue;
1242 XRecolorCursor( mydisplay, fscur, &bgnd, &fgnd);
1243 XDefineCursor( mydisplay, mywindow, fscur );
1245 /* map the window back onscreen with new attributes */
1246 XMapWindow( mydisplay, mywindow );
1248 /* wait and eat notify events until we get MapNotify */
1251 } while ( False == XCheckTypedWindowEvent( mydisplay,
1252 mywindow,
1253 MapNotify,
1254 &xev
1258 /* set focus to the fullscreen window so xinput works.
1259 NOTE: doesn't seem to work if it's in x_init only */
1260 XSetInputFocus( mydisplay,
1261 mywindow,
1262 RevertToNone,
1263 CurrentTime
1266 /* maximize the window */
1267 XMoveResizeWindow( mydisplay,
1268 mywindow,
1269 (attribs.width-attribs.height)/2,
1270 attribs.y,
1271 attribs.height,
1272 attribs.height
1275 /* wait for ConfigureNotify event
1276 have to do this because otherwise x_event() will see
1277 the resize and try to change the BES aspect ratio */
1278 do {
1279 // nano sleep?
1280 } while ( False == XCheckTypedWindowEvent( mydisplay,
1281 mywindow,
1282 ConfigureNotify,
1283 &xev
1287 /* save current overlay settings for later restore,
1288 update current overlay w/ root window attribs, full-screen
1289 Hey Joe!
1291 vw0.x = vw.x;
1292 vw0.y = vw.y;
1293 vw0.width = vw.width;
1294 vw0.height = vw.height;
1296 vw.x = attribs.x;
1297 vw.y = attribs.y;
1298 vw.width = attribs.height; /* root window size */
1299 vw.height = attribs.height; /* root window size */
1301 /* full-screen integer aspect ratio adjustment */
1302 // aspect = (vw0.width<<10)/vw0.height;
1303 // aspect = 1<<10;
1304 // vw.height = (vw.width<<10)/aspect;
1305 // vw.y = ((attribs.height-vw.height)>>1);
1306 vw.x = (attribs.width - attribs.height)/2;
1308 xprintf(stderr,"\tMaximized window x%d y%d w%d h%d\n",
1309 attribs.x, attribs.y, attribs.width, attribs.height);
1310 xprintf(stderr,"\tOverlay window x%d y%d w%d h%d\n",
1311 vw.x, vw.y, vw.width, vw.height);
1313 /* no send_event on XMoveResize, so update BES */
1314 // set_overlay();
1316 } else {
1317 /* restore overlay and window to previous saved.
1318 have to adjust for titlebar height again too */
1319 vw.x = vw0.x;
1320 vw.y = vw0.y;
1321 vw.width = vw0.width;
1322 vw.height = vw0.height;
1323 /* unmap the window so we can change its attributes */
1324 XUnmapWindow( mydisplay, mywindow);
1326 /* wait and eat notify events until we get UnmapNotify */
1329 // nano sleep?
1330 } while ( False == XCheckTypedWindowEvent( mydisplay,
1331 mywindow,
1332 UnmapNotify,
1333 &xev
1337 /* turn on window manager dressing */
1338 nxswa.override_redirect = False;
1339 XChangeWindowAttributes( mydisplay,
1340 mywindow,
1341 nxswamask,
1342 &nxswa
1345 /* put cursor back to normal for windowed */
1346 XUndefineCursor( mydisplay, mywindow);
1348 /* map the window back onscreen with restored attributes */
1349 XMapWindow( mydisplay, mywindow );
1351 /* wait and eat notify events until MapNotify */
1354 // nano sleep?
1355 } while ( False == XCheckTypedWindowEvent( mydisplay,
1356 mywindow,
1357 MapNotify,
1358 &xev
1362 /* restore window size to previously saved BES size,
1363 minus the titlebar height */
1364 XMoveResizeWindow( mydisplay,
1365 mywindow,
1366 vw.x,
1367 vw.y-xtbh,
1368 vw.width,
1369 vw.height
1371 //#if 0
1372 /* wait for and eat the ConfigureNotify event.
1373 have to do this because otherwise x_check_events() will see
1374 the resize and try to change the aspect ratio */
1377 // nano sleep?
1378 } while ( False == XCheckTypedWindowEvent( mydisplay,
1379 mywindow,
1380 ConfigureNotify,
1381 &xev
1385 //#endif
1386 /* no send_event on XMoveResize, so update BES manually */
1387 // set_overlay();
1390 xprintf(stderr,"\tRestored x%d y%d w%d h%d\n",
1391 vw.x, vw.y, vw.width, vw.height);
1393 x_redraw_display(1);
1395 xrszlock = 0; /* unlock this */
1398 /* connect to server, create and map window */
1399 static void
1400 x_init_display( void )
1402 char *dispname = ":0"; /* need to make this option */
1403 XVisualInfo vinfo;
1405 Colormap xcmap;
1406 unsigned long xswamask;
1408 snprintf( xtitle, sizeof(xtitle)-1,
1409 "Strength vs Time Polar Plot %s", in_sname);
1411 /* if the window already exists, return immediately */
1412 if (0 != xinit) {
1413 fprintf(stderr, "X display already open!\n");
1414 return;
1417 /* environment variable DISPLAY will override above definition */
1418 if ( NULL != getenv("DISPLAY")) dispname = getenv("DISPLAY");
1420 /* open the x display */
1421 mydisplay = XOpenDisplay( dispname );
1423 if (mydisplay == NULL) {
1424 perror("opening X display");
1425 exit(1);
1428 /* get default screen number from XOpenDisplay */
1429 myscreen = DefaultScreen( mydisplay );
1431 /* setup x window hints for initial size and position */
1433 hint.x = vw.x;
1434 hint.y = vw.y;
1435 // override hints based on -i value, 0-3, to make window
1436 // start at different x value for each card
1437 hint.x = (16+vw.width) * (1+arg_idev); // scoot it out a bit
1439 hint.width = vw.width;
1440 hint.height = vw.height;
1442 hint.flags = PPosition | PSize;
1444 /* set foreground and background colors */
1445 bg = BlackPixel( mydisplay, myscreen );
1446 fg = WhitePixel( mydisplay, myscreen );
1448 XGetWindowAttributes( mydisplay,
1449 DefaultRootWindow( mydisplay ),
1450 &attribs
1453 xbpp = attribs.depth;
1454 xprintf( stderr, "bpp %d\n", xbpp );
1455 xcolors = xcolors565;
1456 if (24 == xbpp) xcolors = xcolors888;
1457 if (32 == xbpp) xcolors = xcolors888;
1459 /* find a matching visual for the display */
1460 XMatchVisualInfo( mydisplay,
1461 myscreen,
1462 xbpp,
1463 TrueColor,
1464 &vinfo
1467 xprintf( stderr, "visual %lx\n",vinfo.visualid );
1469 /* create the color map for the window */
1470 xcmap = XCreateColormap( mydisplay,
1471 RootWindow( mydisplay, myscreen ),
1472 vinfo.visual,
1473 AllocNone
1476 xswa.background_pixel = BlackPixel( mydisplay, myscreen );
1477 // xswa.foreground_pixel = WhitePixel( mydisplay, myscreen );
1479 xprintf(stderr, "X background %06lX\n", xswa.background_pixel );
1481 xswa.border_pixel = WhitePixel( mydisplay, myscreen );
1482 xswa.colormap = xcmap;
1484 /* create window with background pixel, border pixel and colormap */
1485 xswamask = CWBackPixel
1486 | CWBorderPixel
1487 | CWColormap;
1489 /* create the window on the display */
1490 mywindow = XCreateWindow( mydisplay,
1491 RootWindow( mydisplay, myscreen ),
1492 hint.x,
1493 hint.y,
1494 hint.width,
1495 hint.height,
1497 xbpp,
1498 CopyFromParent,
1499 vinfo.visual,
1500 xswamask,
1501 &xswa
1504 /* set the type of events we will allow in this window
1505 ConfigureNotify move/resize
1506 UnmapNotify overlay off
1507 MapNotify overlay on
1508 KeyPress playback mode key
1509 ButtonPress1 double click to maximize/restore
1510 ***** ButtonPress2 mouse button for cfg dialog (not done)
1512 NOTE: the minimal closer you get isn't event-able, but does close
1513 if you hit it hard enough, use q to quit smoothly
1515 XSelectInput( mydisplay,
1516 mywindow,
1517 StructureNotifyMask
1518 | KeyPressMask
1519 | ButtonPressMask
1522 /* put the window on the display */
1523 XMapWindow(mydisplay, mywindow);
1525 // x_title();
1527 /* wait for map event to indicate the map finished. you can't use
1528 it until then. it will happen after 2 configure events.
1529 (configure size and configure position)
1533 /* wait for window structure events in overlay window */
1534 XWindowEvent( mydisplay,
1535 mywindow,
1536 StructureNotifyMask,
1537 &xev);
1539 /* looking for configure notify events to set size/position */
1540 if (xev.type == ConfigureNotify)
1542 /* window manager may change the actual dimension/position from our hints,
1543 so get where wm actually put window. that's why they're called 'hints' */
1545 xprintf(stderr,"XEvent:\tConfigureNotify\n");
1547 /* send_event indicates window moved, so udpate x,y */
1548 if (xev.xconfigure.send_event)
1550 /* FIXME: this is asking for trouble */
1551 /* get the titlebar height */
1552 xtbh = xev.xconfigure.y - 1;
1554 vw.x = xev.xconfigure.x;
1555 vw.y = xev.xconfigure.y;
1556 vw.width = xev.xconfigure.width;
1557 vw.height = xev.xconfigure.height;
1559 xprintf(stderr,
1560 "\tSend Event x%d y%d w%d h%d th%d\n",
1561 vw.x, vw.y, vw.width, vw.height, xtbh);
1563 } else {
1564 /* otherwise it's a window resize, so update w,h */
1565 vw.width = xev.xconfigure.width;
1566 vw.height = xev.xconfigure.height;
1568 xprintf(stderr,"\tUser w%d h%d\n",
1569 vw.width, vw.height);
1572 /* keep checking events until MapNotify event says window is mapped */
1573 } while (xev.type != MapNotify );
1575 xprintf(stderr,"XEvent:\tMapNotify\n");
1577 /* flush the output buffer and wait until all requests processed.
1578 this is probably not needed. see man XSync for reason why */
1579 XSync(mydisplay, True); /* discard rest of events in output queue */
1582 /* GC needed for any kind of text/gfx overlay, like play status etc */
1584 /* create a graphic context */
1585 mygc = XCreateGC(mydisplay, mywindow, 0L, &xgcv);
1587 XSetBackground(mydisplay, mygc, bg);
1588 XSetForeground(mydisplay, mygc, fg);
1590 /* this is a pretty sharp and legible font */
1591 myxfonts = XLoadQueryFont(mydisplay, "10x20");
1592 if ( myxfonts == (XFontStruct*)NULL ){
1593 fprintf(stderr,"can't load 10x20 font trying fixed\n");
1594 myxfonts = XLoadQueryFont(mydisplay,"fixed");
1595 if ( myxfonts == (XFontStruct*)NULL) {
1596 fprintf(stderr,"can't load fixed font\n");
1598 } else {
1599 XSetFont(mydisplay, mygc, myxfonts->fid);
1600 xprintf(stderr,"X font loaded\n");
1603 /* don't need this for anything yet */
1604 #ifdef NEVER
1605 /* get pointer to graphic context so we can get address of
1606 pixmap data area (possible uses: some type of pixmap overlay) */
1607 myximage = XGetImage( mydisplay,
1608 mywindow,
1611 vw.width,
1612 vw.height,
1613 AllPlanes,
1614 ZPixmap
1616 ximagedata = myximage->data;
1617 #endif
1619 xprintf(stderr,"X created GC %p @ %p\n",
1620 myximage, ximagedata);
1622 /* show the results of what the overlay and X window were set to */
1623 xprintf(stderr,"X display init: %0dx%0d @ %0d,%0d\n",
1624 vw.width, vw.height, vw.x, vw.y);
1626 /* indicate the window is already initialized */
1627 xinit = 1;
1629 x_title();
1631 /* set focus so xinput works when in fullscreen mode */
1632 XSetInputFocus( mydisplay,
1633 mywindow,
1634 RevertToNone,
1635 CurrentTime
1638 if (xmaxi) {
1639 fprintf(stderr,"going full screen\n");
1640 x_resize_display();
1644 /* destroy the window, close the display and clear the init flag */
1645 static void
1646 x_close_display( void )
1648 XDestroyWindow(mydisplay, mywindow);
1649 XCloseDisplay(mydisplay);
1650 xinit = 0;
1655 /* check for the following events:
1656 resize/move = update BES
1657 iconify/restore = disable/enable BES (and pause)
1658 double click = toggles maximize on/off
1659 keypress = set playback mode
1661 static void
1662 x_check_events( void )
1664 /* look for UnmapNotify for minimized/disable overlay */
1665 if ( XCheckTypedWindowEvent( mydisplay,
1666 mywindow,
1667 UnmapNotify,
1668 &xev) )
1671 xprintf(stderr,"\nXEvent:\tUnmapNotifyEvent BES OFF");
1674 /* look for ConfigureNotify for resize or move */
1675 if ( XCheckTypedWindowEvent( mydisplay,
1676 mywindow,
1677 ConfigureNotify,
1678 &xev) )
1681 xprintf(stderr,"\nXEvent:\tConfigureNotify\n");
1683 /* ConfigureNotify event indicates size/position change */
1684 if (xev.type == ConfigureNotify) {
1685 /* send_event indicates window manager move/resize,
1686 so udpate w,h,x,y with new location */
1687 if (xev.xconfigure.send_event) {
1688 vw.x = xev.xconfigure.x;
1689 vw.y = xev.xconfigure.y;
1690 // not square
1691 // vw.width = xev.xconfigure.width;
1693 vw.width = xev.xconfigure.width;
1694 vw.height = xev.xconfigure.height;
1696 xprintf(stderr,"\tSend Event x%d y%d w%d h%d\n",
1697 vw.x, vw.y, vw.width, vw.height);
1698 } else {
1699 /* otherwise it's a user resize, so update w,h */
1700 // not square
1701 // vw.width = xev.xconfigure.width;
1703 vw.width = xev.xconfigure.height;
1704 vw.height = xev.xconfigure.height;
1706 xprintf(stderr,"\tUser w%d h%d\n",
1707 vw.width, vw.height);
1710 x_redraw_display(1);
1715 /* it's ordered like this so the ConfigureNotify above puts overlay
1716 back in right location before enabling it. looks smoother.
1718 /* look for MapNotify for maximized/enable overlay */
1719 if ( XCheckTypedWindowEvent( mydisplay,
1720 mywindow,
1721 MapNotify,
1722 &xev) )
1724 xprintf(stderr,"\nXEvent:\tMapNotify BES ON\n");
1727 /* look for double click events */
1728 if ( XCheckTypedWindowEvent( mydisplay,
1729 mywindow,
1730 ButtonPress,
1731 &xev) )
1733 /* FIXME: need to check for the left button only */
1734 if (xev.xbutton.button == 1) {
1736 xprintf(stderr,"\nXEvent:\tButtonPress\n");
1738 xlbtd = xev.xbutton.time - xlbt0;
1739 xlbt0 = xev.xbutton.time;
1740 if (abs(xlbtd) < 200) {
1741 xprintf(stderr,"\tclick delta %d\n", xlbtd);
1743 xmaxi = ~xmaxi;
1744 x_resize_display();
1749 /* look for keypress events */
1750 if ( XCheckTypedWindowEvent( mydisplay,
1751 mywindow,
1752 KeyPress,
1753 &xev)
1756 /* trying a larger buffer for holding down single step */
1757 int key_buffer_size = 256;
1758 char key_buffer[key_buffer_size-1];
1759 XComposeStatus compose_status;
1760 KeySym key_sym;
1761 int keyval;
1763 /* try to convert key event into ASCII */
1764 XLookupString( (XKeyEvent *)&xev,
1765 key_buffer,
1766 key_buffer_size,
1767 &key_sym,
1768 &compose_status
1771 keyval = key_buffer[0];
1773 xprintf(stderr,"\nXEvent:\tKeyPress '%c' = 0x%X\n",
1774 keyval, keyval);
1776 // NOTE: ADD MORE KEYS HERE
1777 if (keyval > 0) {
1778 switch(keyval) {
1780 case 'q':
1781 case 27:
1782 c_exit(0);
1783 break;
1785 default:
1786 break;
1793 #ifdef USE_DVB
1794 /************************************************************** DVB setup */
1795 static
1797 init_dvb_frontend (int fe_fd, struct dvb_frontend_parameters *frontend)
1799 struct dvb_frontend_info fe_info;
1801 if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) {
1802 fprintf( stderr, "FE GET INFO fail %s\n", strerror(errno));
1803 return -1;
1806 if (fe_info.type != FE_ATSC) {
1807 fprintf( stderr, "FE not an ATSC (VSB/QAM) device\n" );
1808 return -1;
1811 /* No inversion, whatever that is. */
1812 frontend->inversion = INVERSION_OFF;
1814 /* Make sure we have dvb_frontend_info.u.vsb.modulation correctly set,
1815 to get a valid stream.
1817 frontend->u.vsb.modulation = VSB_8;
1818 /* TODO_DVB: does this need to be configurable? */
1819 // not for cable, no VSB 16 planned for terrestrial AFAIK -ink
1820 return 0;
1823 static
1825 set_dvb_channel( int i )
1827 int ir = 0;
1828 unsigned int f = 0;
1830 freq = atsc_bcast[i].freq;
1832 if (freq == pfreq) return 0;
1833 pfreq = freq;
1835 /* See dtvsignal.c in:
1836 * http://www.pchdtv.com/downloads/dvb-atsc-tools-0.tar.gz
1837 * for where the "+ 1750" calculation comes from.
1839 f = ( freq + 1750 ) * 1000; /* freq is in kHz. We need it in Hz for DVB API */
1841 dvb_frontend_param.frequency = f;
1842 ir = ioctl( in_file, FE_SET_FRONTEND, &dvb_frontend_param );
1843 if (ir != 0)
1844 fprintf( stderr, "FE SET FRONTEND fail %d %s\n", ir, strerror( errno ) );
1846 return ir;
1848 #endif
1850 #ifdef USE_V4L
1851 static
1853 set_v4l2_channel( int i )
1855 int ir;
1856 struct v4l2_frequency v2f;
1858 freq = ((atsc_bcast[i].freq/10)*16)/100; /* divide by 62.5 */
1859 // inhibit multiple calls so tuner tank remains stabilized
1860 if (pfreq == freq) return 0;
1861 pfreq = freq;
1862 ir = 0;
1864 // fprintf( stdout, "channel = %d freq*16 = %ld ",i ,freq);
1865 // v4l2 needs some slight init
1866 v2f.frequency = freq;
1867 v2f.type = V4L2_TUNER_ANALOG_TV;
1868 v2f.tuner = 0; // one tuner
1869 ir = ioctl( in_file, VIDIOC_S_FREQUENCY, &v2f );
1870 return ir;
1873 static
1875 set_v4l1_channel( int i )
1877 int ir;
1879 freq = ((atsc_bcast[i].freq/10)*16)/100; /* divide by 62.5 */
1880 // inhibit multiple calls so tuner tank remains stabilized
1881 if (pfreq == freq) return 0;
1882 pfreq = freq;
1883 ir = 0;
1885 // fprintf( stdout, "channel = %d freq*16 = %ld ",i ,freq);
1886 ir = ioctl( in_file, VIDIOCSFREQ, &freq );
1887 return ir;
1889 #endif
1891 static
1893 set_channel( int i )
1895 int ir;
1897 ir = 0;
1899 #ifdef USE_V4L
1900 if (1 == arg_vapi) ir = set_v4l1_channel( i );
1901 if (2 == arg_vapi) ir = set_v4l2_channel( i );
1902 #endif
1904 #ifdef USE_DVB
1905 if (3 == arg_vapi) ir = set_dvb_channel( i );
1906 #endif
1908 nanosleep( &tune_sleep, NULL );
1909 return ir;
1912 #ifdef USE_DVB
1913 static
1914 void
1915 get_dvb_strength( void )
1917 int ir = 0;
1918 fe_status_t status;
1919 unsigned short sig;
1921 ledG = sig1 = sig2 = siglock = sigper = sig = 0;
1922 ledR = 1;
1923 ir = 0;
1924 strength = 0;
1926 ir = ioctl( in_file, FE_READ_STATUS, &status );
1927 if (ir < 0) {
1928 fprintf( stderr, "FE READ STATUS fail %s\n", strerror(errno) );
1929 return;
1932 siglock = (status & FE_HAS_LOCK) ? 1:0;
1934 ir = ioctl(in_file, FE_READ_SIGNAL_STRENGTH, &sig);
1935 if (ir < 0) {
1936 fprintf( stderr, "FE_READ_SIGNAL_STRENGTH fail %d %s\n",
1937 ir, strerror(errno));
1938 return;
1941 // without a sleep, will display fast and nothing if no lock
1942 // nanosleep( &sig_sleep, NULL);
1944 // scale to 0-100 range
1945 strength = sig / 655;
1947 if (strength > 100) strength = 100;
1949 sigper = strength;
1950 strength_count++; // good ioctl
1951 strength_sum += strength; // add the percentage values for average
1953 return;
1956 #endif
1959 #ifdef USE_V4L
1960 static
1961 void
1962 get_v4l2_strength( void )
1964 int ir;
1966 ledG = sig1 = sig2 = siglock = sigper = 0;
1967 ledR = 1;
1968 ir = 0;
1969 strength = 0;
1971 memset( &v2sig, 0, sizeof( v2sig ) );
1972 ir = ioctl( in_file, VIDIOC_G_TUNER, &v2sig );
1973 if (0 != ir ) return; // bad ioctl
1975 strength = v2sig.signal; // V4L2 has better math
1976 if (strength > 40) {
1977 siglock = ledG = 1; // fake
1978 ledR = 0; // fake
1980 sigper = strength;
1982 strength_count++; // good ioctl
1983 strength_sum += sigper; // add the percentage values for average
1985 return;
1988 static
1989 void
1990 get_v4l1_strength( void )
1992 int ir;
1994 ledG = sig1 = sig2 = siglock = sigper = 0;
1995 ledR = 1;
1996 strength = 0;
1998 ir = 0;
2000 memset( &vsig, 0, sizeof( vsig ) );
2001 ir = ioctl( in_file, VIDIOCGSIGNAL, &vsig );
2002 if (0 != ir) return; // bad ioctl
2004 strength = (0xFF0000 & vsig.strength) >> 16;
2005 siglock = ( (0x43 & vsig.strength) == 0x43 ) ? 1:0;
2006 sig1 = 0xFF & (0x100 - ((0xFF00 & vsig.strength)>>8));
2007 sig2 = 0xFF & (0x100 - (0xFF & (vsig.aux)));
2009 ledR = 1 & (vsig.aux>>31); // custom hd2000 driver only
2010 ledG = 1 & (vsig.aux>>30); // custom hd2000 driver only
2012 sigper = (sig1 * 100) / 240; // bad math but gives something
2014 strength_count++; // good ioctl
2015 strength_sum += sigper; // add the percentage values for average
2017 #endif
2019 // sets global strength variables and sum and count for averaging
2020 static
2021 void
2022 get_strength( void )
2025 #ifdef USE_V4L
2026 if (1 == arg_vapi) get_v4l1_strength();
2027 if (2 == arg_vapi) get_v4l2_strength();
2028 #endif
2030 #ifdef USE_DVB
2031 if (3 == arg_vapi) get_dvb_strength();
2032 #endif
2037 static
2038 void
2039 scan_loop( void )
2041 int i, ir;
2042 int j, k, m, n;
2044 unsigned char bar[132]; // 80 cols at least
2045 unsigned int barx;
2046 double fm;
2047 int sfreq;
2049 ir = 0;
2051 if (scan_count != 0) {
2052 k = scan_count;
2053 } else {
2054 k = 1;
2057 if (arg_otxt) {
2059 fprintf( stderr, "\033[1;1H\033[2J"); //clear
2060 fprintf( stderr, "\033[4;24r"); // set scroll region
2061 fprintf( stderr, "\033[1;1H i Ch Call Net MHz dB%% SS%% SNR AGC IR STR2 STR1 LGR ERRORS %s", in_sname );
2062 fprintf( stderr, "\033[3;1H %%| NONE JUNK ECHO SKIP POOR FAIR GOOD BEST\n");
2066 x_redraw_display(1);
2067 while(1)
2069 for (j = 0; j < k; j++)
2071 i = scan_list[j];
2072 chan = i;
2073 set_channel( chan );
2075 x_redraw_display(1);
2076 // fprintf( stderr, "\033[4;1H\033[J");
2078 // clear old junk and redraw text and grid
2079 strength = 0;
2081 #define PI360 1131
2083 // fflush(stdout); // move move move
2085 for( m = 0; m < (PI360 * arg_loop); m++ )
2087 // nanosleep( &sig_sleep, NULL );
2089 // loop start resets time stats
2090 if (0 == (m % PI360)) {
2092 x_redraw_display( arg_wipe );
2094 memset( &loop_start, 0, sizeof(loop_start));
2095 memset( &loop_stop, 0, sizeof(loop_start));
2096 memset( &loop_diff, 0, sizeof(loop_start));
2099 /* Peter Knaggs Peter.Knaggs@gmail.com reports the following odd glibc error:
2100 * Debian seems to return zero from PROCESS_CPUTIME_ID, even though my
2101 * kernel is compiled with CONFIG_HPET_TIMER. Using the syscall directly
2102 * via syscall(__NR_clock_gettime, PROCESS_CPUTIME_ID, &loop_start) works.
2103 * Perhaps it's simpler to use CLOCK_REALTIME in this case, though.
2105 // clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &loop_start );
2106 clock_gettime( CLOCK_REALTIME, &loop_start );
2108 strength_count = 0;
2109 strength_sum = 0;
2110 strength_avg = 0;
2114 // loop stop displays time stats
2115 if ( (PI360 - 1) == (m % PI360) ) {
2116 long long nanos;
2117 long long snano;
2118 nanos = snano = 0;
2119 // see above
2120 // clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &loop_stop );
2121 clock_gettime( CLOCK_REALTIME, &loop_stop );
2123 time_diff( &loop_diff, &loop_start, &loop_stop );
2125 // fprintf( stdout, "start %d.%09ld stop %d.%09ld\n",
2126 // (int)loop_start.tv_sec, loop_start.tv_nsec,
2127 // (int)loop_stop.tv_sec, loop_stop.tv_nsec );
2130 // TESTME: forcing type, may be bad for 64 bit
2131 nanos = 1000000000ULL * (unsigned long)loop_diff.tv_sec;
2132 nanos += (unsigned long)loop_diff.tv_nsec;
2134 /* avoid divide by zero */
2135 if (0 != strength_count) {
2136 snano = nanos / strength_count;
2137 snano = 1000000000 / snano; // per second
2138 // if (0 != arg_otxt) fprintf( stderr, "\033[25;1H" );
2139 fprintf( stderr,
2140 "\n%s channel %2d, sig %2d avg, %2lld scans/s, 360 time %d.%09lds\n\n",
2141 in_sname, chan, strength_sum / strength_count, snano,
2142 (int)loop_diff.tv_sec, loop_diff.tv_nsec );
2143 if (0 == arg_otxt) fprintf( stderr, "\n");
2144 // if (0 != arg_otxt) fprintf( stderr, "\033[24;1H" );
2150 if (0 == (m % arg_modu))
2151 get_strength();
2153 sfreq = atsc_bcast[i].freq / 1000;
2154 if (3 == arg_vapi) sfreq = (atsc_bcast[i].freq+1750) / 1000;
2156 if (0 != arg_otxt) {
2157 #if 0
2158 fprintf( stderr,
2159 "\033[s\033[2;1H"
2160 "%3d %-11s %3d %3d%% %3d%% %04X %04X %2d "
2161 "%08X %08X %1X%1X%1X %2s%2s%2s"
2162 "\033[K\033[u",
2163 m % 999, atsc_bcast[i].sid, sfreq,
2164 strength, sigper, sig1, sig2, ir,
2166 // FIXME no leds, shorten, unjunk display
2167 #ifdef USE_V4L
2168 v2sig.signal, vsig.strength, siglock, ledG, ledR,
2169 #else
2170 -1, -1, siglock, ledG, ledR,
2171 #endif
2172 (siglock==0) ? "-L":"",
2173 (ledG==0) ? "-G":"",
2174 (ledR!=0) ? "+R":"" );
2175 #endif
2177 if (strength < 0) strength = 0;
2178 if (strength > 100) strength = 100;
2179 barx = (strength * 75) / 100; // 75 chars max
2180 if (barx > 0) memset( bar, '*', barx);
2181 bar[barx] = 0;
2182 fprintf( stderr, "%3d%% %s\n", strength, bar);
2185 if (0 != arg_adsp) {
2187 n = strength / 2;
2188 n += 24;
2189 // if (n > 47) n = 47;
2191 if (n < 0) n = 0;
2193 build_semitone( n );
2195 // no audio until past 22%
2196 if (strength > arg_athr) play_audio( strength );
2199 // strength = (m/12) % 100; // calibrate check
2202 // X11 functions conditional
2203 if (arg_ox11) {
2204 x_check_events();
2206 if (strength > 99) strength = 99;
2207 fstrength = ((double)strength) / 100.0;
2208 fxscale = (double)vw.width / 2.0;
2209 // room for 3 lines of 20 pels
2210 fyscale = (double)vw.height / 2.0;
2211 xorigin = vw.width / 2;
2212 yorigin = vw.height / 2;
2214 // dummy max for roundness check
2215 // xend = xscale * sin( m / 180.0 );
2216 // yend = yscale * cos( m / 180.0 );
2218 // black/grey line ahead of next line
2219 fm = 0.0;
2220 fm = ((double)(m+1.0)) / 180.0;
2221 xend = fxscale * sin( fm );
2222 yend = fyscale * cos( fm );
2224 bg = xcolors[9]; // even circles dark grey
2225 if (1 == (1 & (m/PI360))) {
2226 bg = 0; // odd circles black
2227 // while(1); // calibration check
2229 XSetBackground( mydisplay, mygc, bg );
2230 XSetForeground( mydisplay, mygc, bg );
2231 XDrawLine( mydisplay, mywindow, mygc,
2232 xorigin,
2233 yorigin,
2234 xorigin + xend,
2235 yorigin - yend
2237 #if 0
2238 // second line?
2239 fm = 0.0;
2240 fm = ((double)m) / 180.0;
2242 xend = fxscale * sin( fm );
2243 yend = fyscale * cos( fm );
2244 // XSetForeground( mydisplay, mygc, fg );
2245 XDrawLine( mydisplay, mywindow, mygc,
2246 xorigin,
2247 yorigin,
2248 xorigin + xend,
2249 yorigin - yend
2251 #endif
2253 fg = xcolors[0];
2255 if (strength > 12) fg = xcolors[1];
2256 if (strength > 24) fg = xcolors[2];
2257 if (strength > 36) fg = xcolors[3];
2258 if (strength > 48) fg = xcolors[4];
2259 if (strength > 60) fg = xcolors[5];
2260 if (strength > 72) fg = xcolors[6];
2261 if (strength > 84) fg = xcolors[7];
2263 xend = fstrength * fxscale * sin( fm );
2264 yend = fstrength * fyscale * cos( fm );
2266 //xorigin += fxscale*sin(fstrength);
2267 //yorigin -= fyscale*cos(fstrength);
2269 XSetForeground( mydisplay, mygc, fg );
2270 XDrawLine( mydisplay, mywindow, mygc,
2271 xorigin,
2272 yorigin,
2273 xorigin + xend,
2274 yorigin - yend
2277 x_display_grid();
2279 #if 0
2280 // this works ok, for horizontal overwriting histogram, also is lame
2281 XSetForeground( mydisplay, mygc, bg );
2282 // blank the line ahead
2283 if (m < (vw.width-1))
2284 XDrawLine( mydisplay, mywindow, mygc,
2285 m+1, vw.height, m+1, vw.height-yscale);
2287 XDrawLine( mydisplay,mywindow,mygc,
2288 m, vw.height, m, vw.height-(yscale * fstrength) );
2290 XFlush( mydisplay );
2291 #endif
2293 // nanosleep( &sig_sleep, NULL );
2298 if (arg_once) c_exit(0);
2300 // fprintf(stderr, "\n");
2302 // fprintf(stderr, "\033[%dA", k);
2306 #if 0
2307 // 2 to the power of 1/12 is semitone step on chromatic scale
2308 static
2309 void
2310 build_chromatic_root_A_55hz( void ) {
2311 double chromatic = 1.059463094; // 2 to power of 1/12
2312 double semitone = 55.0;
2313 int i,j;
2315 // create 8 octaves of 12 chromatic semi tones, starting at 220 A
2316 fprintf( stdout, " A A#/Bb B C C#/Db D D#/Eb F F#/Gb G G#/Ab\n");
2317 for (i = 0; i<8; i++) {
2318 // 220.00
2319 for (j = 0; j<12; j++) {
2320 fprintf( stdout, "%5.0f,", semitone);
2321 semitone *= chromatic;
2323 fprintf( stdout, "\n");
2326 #endif
2327 static
2328 void
2329 usage( char ** argv )
2331 fprintf( stdout,
2332 " Usage:\n"
2333 " %s [options] channel [channel2 ... channel82]\n"
2334 "\n"
2335 " Where:\n"\
2336 " channel is numeric 2 through 82\n"
2337 " # is numeric 1 to n\n"
2338 "\n"
2339 " Keys:\n"
2340 " q quit program\n"
2341 " double click button 1 (LMB) for maximize\n"
2342 "\n"
2343 " Options:\n"
2344 #ifdef USE_V4L
2345 " -1 use V4L API for signal strength\n"
2346 " -2 use V4L2 API\n"
2347 " -3 use DVB API [default]\n"
2348 " -i # set input device number [/dev/dtv#]\n"
2349 #endif
2350 " -i # set input device number [/dev/dvb/adapter#]\n"
2351 " -b # signal strength below which audio is silent\n"
2352 " -r # radians per scan\n"
2353 " -l # number of 360 degree loops\n"
2354 " -w # width and height for window\n"
2355 " -s # sleep time per scan\n"
2356 " -a use audio feedback\n"
2357 " -t text feedback only, no X display\n"
2358 " -x X visual feedback only, no text [default]\n"
2359 " -y debug X events\n"
2360 " -c clear display after each loop\n"
2361 "\n", argv[0]);
2364 static
2365 void
2366 c_exit( int ev )
2368 if (0 != xinit) x_close_display();
2369 if (0 != arg_adsp) close_audio();
2370 if (in_file > 0) close(in_file);
2371 // reset term to clear scrolling region
2372 if (0 != arg_otxt) fprintf( stderr, "\033c");
2373 exit( ev );
2376 // arg_vapi determines correct device and init method
2377 static
2378 void
2379 init_device( void )
2381 int ir;
2382 ir =0;
2384 // set device name
2385 if ( 3 == arg_vapi ) {
2386 snprintf( in_name, sizeof(in_name)-1, "/dev/dvb/adapter%d/frontend%d", arg_idev, 0);
2387 } else {
2388 snprintf( in_name, sizeof(in_name)-1, "/dev/dtv%d", arg_idev);
2391 // open device
2392 in_file = open (in_name, O_RDWR );
2393 if ( 0 > in_file ) {
2395 fprintf( stderr,
2396 "Could not open file %s - %s\n",
2397 in_name, strerror( errno ));
2399 exit( 1 );
2402 #ifdef USE_DVB
2403 // DVB init
2404 if ( 3 == arg_vapi ) {
2405 ir = init_dvb_frontend( in_file, &dvb_frontend_param );
2406 if ( ir < 0 )
2408 fprintf( stderr, "Could not set up frontend %s\n", in_name);
2409 exit( 1 );
2411 /* latest DVB API uses this method to fix driver i2c lock-out */
2413 #ifdef FE_SET_FRONTEND_TUNE_MODE
2414 ioctl( in_file, FE_SET_FRONTEND_TUNE_MODE, FE_TUNE_MODE_ONESHOT);
2415 #endif
2418 #endif
2420 #ifdef USE_V4L
2421 if ( 3 != arg_vapi ) {
2422 // V4L needs to set the input source (CHAN is not frequency but source)
2423 // V4l2 is using V4L1 for this until I figure out V4L2 replacement
2424 // HD2000 has two antenna inputs, but not sure how to set them anymore
2425 ir = ioctl( in_file, VIDIOCSCHAN, &vch_ATSC );
2426 if ( 0 != ir ) {
2427 fprintf( stderr, "V4L1 Set Input Source Channel error\n");
2428 exit(1);
2431 #endif
2436 static
2437 void
2438 parse_args( int argc, char ** argv ){
2439 int i,j,k;
2440 int c;
2441 char *t;
2443 if (1 == argc) {
2444 usage( argv );
2445 fprintf( stdout, "Can't scan without a channel.\n\n");
2446 exit( 1 );
2449 while ( EOF != ( c = getopt( argc, argv, "123cqatxyb:w:i:r:s:l:" ) ) )
2451 switch(c) {
2452 case '1':
2453 case '2':
2454 case '3':
2455 arg_vapi = c & 3;
2456 if ( 0 == arg_vapi ) {
2457 fprintf( stderr, "Invalid Video API: use 1 2 or 3\n");
2458 exit(1);
2460 break;
2462 case 'c':
2463 arg_wipe = ~0;
2464 break;
2466 case 'q':
2467 arg_once = ~0;
2468 arg_loop = 1;
2469 break;
2471 // enable audio
2472 case 'a':
2473 arg_adsp = ~0;
2474 break;
2476 // signal percent threshold before audio output
2477 case 'b':
2478 if (NULL != optarg) {
2479 sscanf( optarg,"%d",&arg_athr );
2481 break;
2483 // X event display
2484 case 'e':
2485 arg_xmsg = ~0;
2486 break;
2488 case 'w':
2489 if (NULL != optarg) {
2490 sscanf( optarg, "%d", &arg_xdim );
2492 vw.height = arg_xdim;
2493 vw.width = arg_xdim;
2494 break;
2496 case 'i':
2497 if (NULL != optarg) {
2498 sscanf( optarg, "%d", &arg_idev );
2500 break;
2502 case 'l':
2503 if (NULL != optarg) {
2504 sscanf( optarg, "%d", &arg_loop );
2506 break;
2508 case 's':
2509 if (NULL != optarg) {
2510 sscanf( optarg, "%d", &arg_time );
2511 if (0 != arg_time) sig_sleep.tv_nsec = arg_time;
2513 break;
2515 /* radians */
2516 case 'r':
2517 if (NULL != optarg) {
2518 sscanf( optarg, "%d", &arg_modu );
2519 /* arg_modu = 3.1415926 * arg_modu; */
2521 break;
2523 case 't':
2524 arg_otxt = ~0;
2525 arg_ox11 = 0;
2526 break;
2528 case 'x':
2529 arg_otxt = 0;
2530 arg_ox11 = ~0;
2531 break;
2533 default:
2534 break;
2539 memset( in_sname, 0, sizeof( in_sname ) );
2540 switch(arg_vapi) {
2541 case 1:
2542 t = "Video4Linux";
2543 snprintf( in_sname, sizeof( in_sname), "dtv%d", arg_idev);
2544 break;
2546 case 2:
2547 t = "Video4Linux2";
2548 snprintf( in_sname, sizeof( in_sname), "dtv%d", arg_idev);
2549 break;
2551 case 3:
2552 t = "DVB";
2553 snprintf( in_sname, sizeof( in_sname), "dvb%d", arg_idev);
2554 break;
2556 default:
2557 fprintf(stderr, "\nNO VIDEO API CHOSEN\n");
2558 exit(1);
2559 break;
2562 fprintf( stdout, "Using %s API on %s\n", t, in_sname );
2564 k = optind;
2566 if (0 == (argc - k)) {
2567 usage( argv );
2568 fprintf( stderr, "Can't scan without a channel.\n\n");
2569 exit( 1 );
2572 // fprintf( stdout, "argc - k = %d\n", argc-k);
2574 chan = 0;
2575 if (k < argc) {
2576 chan = atoi(argv[k]);
2577 // fprintf (stdout, "Using channel %d\n",chan);
2580 memset( scan_list, 0, sizeof( scan_list ) );
2581 /* parse arg channel list if exists */
2582 if (k < argc) {
2583 for (i=0; i < 81; i++) {
2584 j = 0;
2585 if (k > argc) break;
2587 if ( argv[ k ] == NULL ) break;
2589 j = atoi( argv[ k++ ] );
2590 if ( (j < 2) || (j > 83) ) {
2591 fprintf( stderr, "Invalid channel %d: range is 2 thru 83\n", j );
2592 continue; // not fatal, skip it
2595 // fprintf( stdout, "scan %d\n", j );
2596 scan_list[ i ] = j;
2597 scan_count++;
2601 if (chan < 0) chan = 0;
2603 in_file = 0;
2604 init_device();
2608 main( int argc, char ** argv )
2611 fprintf(stdout, NAME" "VERSION" "COPYRIGHT" "EMAIL"\n");
2612 fprintf(stdout, "Released under the "LICENSE"\n");
2614 parse_args( argc, argv );
2616 if (0 != arg_adsp) {
2617 init_audio();
2620 // if X not started, fall back to text mode
2621 if ( NULL == getenv( "DISPLAY" ) ) {
2622 arg_ox11 = 0;
2623 arg_otxt = ~0;
2626 if (0 != arg_ox11) {
2627 x_init_display();
2628 x_redraw_display(1);
2631 scan_loop();
2633 fprintf( stderr, "\n" );
2635 if (0 != arg_ox11) {
2636 x_close_display();
2639 if (0 != arg_adsp) {
2640 close_audio();
2643 close( in_file );
2646 return 0;