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"
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 */
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 *******************************************************************************
54 Will set device /dev/dtv0 to UHF channel 19 and begin signal scan
58 Will set device /dev/dtv0 to UHF channel 19 and begin signal scan,
59 with variable audio pitch feedback for signal strength.
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
91 Ignore all the Open GL code. I'll flesh it out someday.
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.
102 #include <sys/types.h>
103 #include <sys/stat.h>
108 #include <sys/ioctl.h>
109 #include <inttypes.h>
113 #include <X11/Xlib.h>
114 #include <X11/Xutil.h>
115 #include <X11/cursorfont.h>
117 #include <sys/soundcard.h>
126 #include <linux/dvb/frontend.h>
127 #include <linux/dvb/dmx.h>
130 /* V4L is deprecated as of june 2006, do not use */
132 #warning obsolete V4L API via -1 -2 options
133 #include <linux/videodev.h>
134 #include <linux/videodev2.h>
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
156 static int arg_vapi
= 3; // 1 = V4L1 2 = V4L2 3 = DVB
160 static int arg_vapi
= 1; // 1 = V4L1 2 = V4L2 3 = DVB
163 /******************************************************** X related globals */
164 static Display
*mydisplay
;
165 static Window mywindow
;
166 static XWindowAttributes attribs
;
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 */
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
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 */
238 static unsigned char in_name
[256];
239 static unsigned char in_sname
[8];
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;
259 // dvb uses to tune to a channel (frequency)
260 struct dvb_frontend_parameters dvb_frontend_param
;
265 static struct video_signal vsig
;
266 static struct v4l2_tuner v2sig
;
268 struct video_channel vch_ATSC
= {
277 // DELETEME: why is this here? leftover?
278 struct video_channel vch_NTSC
= {
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
[] = {
365 // not supposed to have any stations up here but let user try for themselves
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)
406 //#define SAMPLE_SIZE (SAMPLE_RATE * SAMPLE_CHANNELS)
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
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 */
453 static struct mouse_s mouse
;
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
472 //static struct point3f_s point;
477 /*************************************************************** PROTOTYPES */
478 void glutCloseFunc( void * );
479 void glutLeaveMainLoop( void );
482 static void c_exit( int ev
);
484 /******************************************************************* TIMERS */
485 // put timespec nanosecond time difference from z - y into x
488 time_diff( struct timespec
*x
, struct timespec
*y
, struct timespec
*z
)
496 if (y
->tv_nsec
> nsec
) { // borrow implicit second if/when clock wraps
500 x
->tv_nsec
= nsec
- y
->tv_nsec
;
501 x
->tv_sec
= sec
- y
->tv_sec
;
506 /******************************************************** GL/glut FUNCTIONS */
512 theGrid
= glGenLists( 1 );
513 glNewList( theGrid
, GL_COMPILE
);
515 glColor3f ( 0.0, 1.0, 0.0 );
518 glVertex3f( -1.0, -1.0, 0.0 );
519 glVertex3f( 1.0, 1.0, 0.0 );
523 glColor3f ( 1.0, 0.0, 0.0 );
526 glVertex3f( 1.0, -1.0, 0.0 );
527 glVertex3f( -1.0, 1.0, 0.0 );
531 glColor3f ( 1.0, 1.0, 0.0 );
534 glVertex3f( 0.0, 1.0, 0.0 );
535 glVertex3f( 0.0, -1.0, 0.0 );
539 glColor3f ( 1.0, 0.0, 1.0 );
542 glVertex3f( 1.0, 0.0, 0.0 );
543 glVertex3f( -1.0, 0.0, 0.0 );
551 /* initialize rgba mode with antialias, alpha blend, hint, and line width */
556 // if you need to know granularity and max line width
558 // glGetFloatv( GL_LINE_WIDTH_GRANULARITY, v);
559 // glGetFloatv( GL_LINE_WIDTH_RANGE, v);
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 /*///////////////////////////////////////////////////////////////////////////*/
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
592 // nanosleep( &gl_sleep, NULL );
598 GLReshape(int w, int h)
600 glViewport(0, 0, w, h);
601 glMatrixMode( GL_PROJECTION );
603 gluPerspective( 20.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
604 glMatrixMode( GL_MODELVIEW );
610 GLLeftMouseButton( void )
612 fprintf( stdout, "Left Mouse Down\n");
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) {
625 GLReshape( origin.w, origin.h );
631 GLRightMouseButton( void )
633 fprintf( stdout, "Right Mouse Down\n");
638 GLMouse( int b, int u, int x, int 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();
656 GLKeyboard( unsigned char c, int i, int j )
658 fprintf( stdout, "Keyboard: c '%c' i %d j %d\n", c, i, j );
664 fullscreen = ~fullscreen;
665 if (0 != fullscreen) {
668 GLReshape( origin.w, origin.h );
681 fprintf( stdout, "Close: w %d\n", w);
686 // this function gets called when not doing anything.
687 // could make it change the location of the line
692 // change parameters for the line
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
)
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");
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
738 /********************************************************************* AUDIO */
744 audio_fd
= open( OSS_DEVICE
, O_WRONLY
, 0 );
745 if (audio_fd
== -1) {
746 perror( "OPEN ERROR " OSS_DEVICE
);
749 fprintf(stderr
, "Audio open\n");
758 ok
= close(audio_fd
);
760 perror( "CLOSE ERROR " OSS_DEVICE
);
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
770 build_frequency( int afreq
, int clear
)
772 double pi1
= 3.1415926;
774 double samples_cycle
;
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);
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) )
795 k
= 32767 * sin( j
* sample_scale
);
797 k
= audio_sample
[ sample_index
];
798 // try additive for now but probably wrong
799 // k += (32767 * sin( j * sample_scale ));
803 if (k
< -32767) k
= -32767;
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] );
817 // use normal table index to build semitone n (0-11 for first octave)
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)
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
840 int format
, channels
, rate
, ir
;
841 format
= AFMT_S16_NE
;
842 channels
= SAMPLE_CHANNELS
;
846 ir
= ioctl(audio_fd
, SNDCTL_DSP_SETFMT
, &format
);
848 perror( "SNDCTL_DSP_SETFMT ERROR " OSS_DEVICE
);
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
);
857 fprintf( stderr
, "%s SNDCTL_DSP_SETFMT AFMT_S16_LE\n", OSS_DEVICE
);
859 ir
= ioctl(audio_fd
, SNDCTL_DSP_CHANNELS
, &channels
);
861 perror( "SNDCTL_DSP_CHANNELS ERROR " OSS_DEVICE
);
864 fprintf( stderr
, "%s SNDCTL_DSP_CHANNELS\n", OSS_DEVICE
);
867 fprintf( stderr
, "SNDCTL_DSP_CHANNELS NO MONO %s\n", OSS_DEVICE
);
870 fprintf( stderr
, "%s SNDCTL_DSP_CHANNELS has monaural\n", OSS_DEVICE
);
872 ir
= ioctl(audio_fd
, SNDCTL_DSP_SPEED
, &rate
);
874 perror( "SNDCTL_DSP_SPEED ERROR " OSS_DEVICE
);
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);
884 build_tone( 12 ); // should be 110Hz A, 0-11 is inaudible for some
887 // send tone to device
890 play_audio( int duration
) {
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);
903 samp_len
= sample_index
;
905 // most likely will fail and have to check len for how much written
907 len
= write( audio_fd
, audio_sample
, samp_len
);
909 perror( "AUDIO WRITE " OSS_DEVICE
);
913 // fprintf( stderr, "Wrote %d bytes\n", len);
918 static void init_audio( void ) {
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 */
931 x_fontline( int row
)
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);
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
,
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
);
979 x_display_grid( void )
981 int c
, j
, x
, y
, h
, w
;
985 if (0 == xinit
) return;
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);
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
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
,
1033 XFlush( mydisplay
);
1039 // shows which channel is being charted
1042 x_display_channel( void )
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 */
1059 yoffset
= x_fontline(0);
1061 XSetBackground( mydisplay
, mygc
, bg
);
1063 XSetForeground( mydisplay
, mygc
, fg
);
1065 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1066 XDrawImageString( mydisplay
,
1075 XFlush( mydisplay
);
1077 memset( xtext
, 0 , sizeof(xtext
) );
1078 snprintf( xtext
, sizeof(xtext
)-1, "%s", in_sname
);
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
,
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");
1103 yoffset
= vw
.height
- 2;
1105 /* XDrawString draws fg only, XDrawImageString draws fg + bg */
1106 XSetForeground( mydisplay
, mygc
, fg
);
1107 XDrawImageString( mydisplay
,
1115 // draw rectangles for bad color
1116 yoffset
-= x_fontline(0)+9;
1118 x_draw_filled_rectangle( xcolors
[0], xoffset
, yoffset
, 10, 10 );
1120 x_draw_filled_rectangle( xcolors
[1], xoffset
, yoffset
, 10, 10 );
1122 x_draw_filled_rectangle( xcolors
[2], xoffset
, yoffset
, 10, 10 );
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
,
1143 // draw rectangles for good color
1144 yoffset
-= x_fontline(0)+9;
1146 x_draw_filled_rectangle( xcolors
[4], xoffset
, yoffset
, 10, 10 );
1148 x_draw_filled_rectangle( xcolors
[5], xoffset
, yoffset
, 10, 10 );
1150 x_draw_filled_rectangle( xcolors
[6], xoffset
, yoffset
, 10, 10 );
1152 x_draw_filled_rectangle( xcolors
[7], xoffset
, yoffset
, 10, 10 );
1154 XFlush( mydisplay
);
1160 x_clear_display( void )
1162 if (0 == xinit
) return;
1164 XClearWindow( mydisplay
, mywindow
);
1165 XFlush( mydisplay
);
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
1181 /***********************************************************************
1182 * start of main block of X window support functions
1183 * FIXME: all of these functions need some kind of error handling
1184 ***********************************************************************/
1187 x_resize_display( void )
1190 XSetWindowAttributes nxswa
;
1191 unsigned long nxswamask
;
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 */
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
,
1219 /* turn off window manager dressing */
1220 nxswa
.override_redirect
= True
;
1221 XChangeWindowAttributes( mydisplay
,
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 */
1236 fgnd
.flags
= DoRed
| DoGreen
| DoBlue
;
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
,
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
,
1266 /* maximize the window */
1267 XMoveResizeWindow( mydisplay
,
1269 (attribs
.width
-attribs
.height
)/2,
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 */
1280 } while ( False
== XCheckTypedWindowEvent( mydisplay
,
1287 /* save current overlay settings for later restore,
1288 update current overlay w/ root window attribs, full-screen
1293 vw0
.width
= vw
.width
;
1294 vw0
.height
= vw
.height
;
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;
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 */
1317 /* restore overlay and window to previous saved.
1318 have to adjust for titlebar height again too */
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 */
1330 } while ( False
== XCheckTypedWindowEvent( mydisplay
,
1337 /* turn on window manager dressing */
1338 nxswa
.override_redirect
= False
;
1339 XChangeWindowAttributes( mydisplay
,
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 */
1355 } while ( False
== XCheckTypedWindowEvent( mydisplay
,
1362 /* restore window size to previously saved BES size,
1363 minus the titlebar height */
1364 XMoveResizeWindow( mydisplay
,
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 */
1378 } while ( False
== XCheckTypedWindowEvent( mydisplay
,
1386 /* no send_event on XMoveResize, so update BES manually */
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 */
1400 x_init_display( void )
1402 char *dispname
= ":0"; /* need to make this option */
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 */
1413 fprintf(stderr
, "X display already open!\n");
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");
1428 /* get default screen number from XOpenDisplay */
1429 myscreen
= DefaultScreen( mydisplay
);
1431 /* setup x window hints for initial size and position */
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
),
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
,
1467 xprintf( stderr
, "visual %lx\n",vinfo
.visualid
);
1469 /* create the color map for the window */
1470 xcmap
= XCreateColormap( mydisplay
,
1471 RootWindow( mydisplay
, myscreen
),
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
1489 /* create the window on the display */
1490 mywindow
= XCreateWindow( mydisplay
,
1491 RootWindow( mydisplay
, myscreen
),
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
,
1522 /* put the window on the display */
1523 XMapWindow(mydisplay
, mywindow
);
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
,
1536 StructureNotifyMask
,
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
;
1560 "\tSend Event x%d y%d w%d h%d th%d\n",
1561 vw
.x
, vw
.y
, vw
.width
, vw
.height
, xtbh
);
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");
1599 XSetFont(mydisplay
, mygc
, myxfonts
->fid
);
1600 xprintf(stderr
,"X font loaded\n");
1603 /* don't need this for anything yet */
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
,
1616 ximagedata
= myximage
->data
;
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 */
1631 /* set focus so xinput works when in fullscreen mode */
1632 XSetInputFocus( mydisplay
,
1639 fprintf(stderr
,"going full screen\n");
1644 /* destroy the window, close the display and clear the init flag */
1646 x_close_display( void )
1648 XDestroyWindow(mydisplay
, mywindow
);
1649 XCloseDisplay(mydisplay
);
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
1662 x_check_events( void )
1664 /* look for UnmapNotify for minimized/disable overlay */
1665 if ( XCheckTypedWindowEvent( mydisplay
,
1671 xprintf(stderr
,"\nXEvent:\tUnmapNotifyEvent BES OFF");
1674 /* look for ConfigureNotify for resize or move */
1675 if ( XCheckTypedWindowEvent( mydisplay
,
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
;
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
);
1699 /* otherwise it's a user resize, so update w,h */
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
,
1724 xprintf(stderr
,"\nXEvent:\tMapNotify BES ON\n");
1727 /* look for double click events */
1728 if ( XCheckTypedWindowEvent( mydisplay
,
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
);
1749 /* look for keypress events */
1750 if ( XCheckTypedWindowEvent( mydisplay
,
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
;
1763 /* try to convert key event into ASCII */
1764 XLookupString( (XKeyEvent
*)&xev
,
1771 keyval
= key_buffer
[0];
1773 xprintf(stderr
,"\nXEvent:\tKeyPress '%c' = 0x%X\n",
1776 // NOTE: ADD MORE KEYS HERE
1794 /************************************************************** DVB setup */
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
));
1806 if (fe_info
.type
!= FE_ATSC
) {
1807 fprintf( stderr
, "FE not an ATSC (VSB/QAM) device\n" );
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
1825 set_dvb_channel( int i
)
1830 freq
= atsc_bcast
[i
].freq
;
1832 if (freq
== pfreq
) return 0;
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
);
1844 fprintf( stderr
, "FE SET FRONTEND fail %d %s\n", ir
, strerror( errno
) );
1853 set_v4l2_channel( int i
)
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;
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
);
1875 set_v4l1_channel( int i
)
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;
1885 // fprintf( stdout, "channel = %d freq*16 = %ld ",i ,freq);
1886 ir
= ioctl( in_file
, VIDIOCSFREQ
, &freq
);
1893 set_channel( int i
)
1900 if (1 == arg_vapi
) ir
= set_v4l1_channel( i
);
1901 if (2 == arg_vapi
) ir
= set_v4l2_channel( i
);
1905 if (3 == arg_vapi
) ir
= set_dvb_channel( i
);
1908 nanosleep( &tune_sleep
, NULL
);
1915 get_dvb_strength( void )
1921 ledG
= sig1
= sig2
= siglock
= sigper
= sig
= 0;
1926 ir
= ioctl( in_file
, FE_READ_STATUS
, &status
);
1928 fprintf( stderr
, "FE READ STATUS fail %s\n", strerror(errno
) );
1932 siglock
= (status
& FE_HAS_LOCK
) ? 1:0;
1934 ir
= ioctl(in_file
, FE_READ_SIGNAL_STRENGTH
, &sig
);
1936 fprintf( stderr
, "FE_READ_SIGNAL_STRENGTH fail %d %s\n",
1937 ir
, strerror(errno
));
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;
1950 strength_count
++; // good ioctl
1951 strength_sum
+= strength
; // add the percentage values for average
1962 get_v4l2_strength( void )
1966 ledG
= sig1
= sig2
= siglock
= sigper
= 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
1982 strength_count
++; // good ioctl
1983 strength_sum
+= sigper
; // add the percentage values for average
1990 get_v4l1_strength( void )
1994 ledG
= sig1
= sig2
= siglock
= sigper
= 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
2019 // sets global strength variables and sum and count for averaging
2022 get_strength( void )
2026 if (1 == arg_vapi
) get_v4l1_strength();
2027 if (2 == arg_vapi
) get_v4l2_strength();
2031 if (3 == arg_vapi
) get_dvb_strength();
2044 unsigned char bar
[132]; // 80 cols at least
2051 if (scan_count
!= 0) {
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);
2069 for (j
= 0; j
< k
; j
++)
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
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
);
2114 // loop stop displays time stats
2115 if ( (PI360
- 1) == (m
% PI360
) ) {
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" );
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
))
2153 sfreq
= atsc_bcast
[i
].freq
/ 1000;
2154 if (3 == arg_vapi
) sfreq
= (atsc_bcast
[i
].freq
+1750) / 1000;
2156 if (0 != arg_otxt
) {
2160 "%3d %-11s %3d %3d%% %3d%% %04X %04X %2d "
2161 "%08X %08X %1X%1X%1X %2s%2s%2s"
2163 m
% 999, atsc_bcast
[i
].sid
, sfreq
,
2164 strength
, sigper
, sig1
, sig2
, ir
,
2166 // FIXME no leds, shorten, unjunk display
2168 v2sig
.signal
, vsig
.strength
, siglock
, ledG
, ledR
,
2170 -1, -1, siglock
, ledG
, ledR
,
2172 (siglock
==0) ? "-L":"",
2173 (ledG
==0) ? "-G":"",
2174 (ledR
!=0) ? "+R":"" );
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
);
2182 fprintf( stderr
, "%3d%% %s\n", strength
, bar
);
2185 if (0 != arg_adsp
) {
2189 // if (n > 47) n = 47;
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
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
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
,
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
,
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
,
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
);
2293 // nanosleep( &sig_sleep, NULL );
2298 if (arg_once
) c_exit(0);
2300 // fprintf(stderr, "\n");
2302 // fprintf(stderr, "\033[%dA", k);
2307 // 2 to the power of 1/12 is semitone step on chromatic scale
2310 build_chromatic_root_A_55hz( void ) {
2311 double chromatic
= 1.059463094; // 2 to power of 1/12
2312 double semitone
= 55.0;
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
++) {
2319 for (j
= 0; j
<12; j
++) {
2320 fprintf( stdout
, "%5.0f,", semitone
);
2321 semitone
*= chromatic
;
2323 fprintf( stdout
, "\n");
2329 usage( char ** argv
)
2333 " %s [options] channel [channel2 ... channel82]\n"
2336 " channel is numeric 2 through 82\n"
2337 " # is numeric 1 to n\n"
2341 " double click button 1 (LMB) for maximize\n"
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"
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"
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");
2376 // arg_vapi determines correct device and init method
2385 if ( 3 == arg_vapi
) {
2386 snprintf( in_name
, sizeof(in_name
)-1, "/dev/dvb/adapter%d/frontend%d", arg_idev
, 0);
2388 snprintf( in_name
, sizeof(in_name
)-1, "/dev/dtv%d", arg_idev
);
2392 in_file
= open (in_name
, O_RDWR
);
2393 if ( 0 > in_file
) {
2396 "Could not open file %s - %s\n",
2397 in_name
, strerror( errno
));
2404 if ( 3 == arg_vapi
) {
2405 ir
= init_dvb_frontend( in_file
, &dvb_frontend_param
);
2408 fprintf( stderr
, "Could not set up frontend %s\n", in_name
);
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
);
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
);
2427 fprintf( stderr
, "V4L1 Set Input Source Channel error\n");
2438 parse_args( int argc
, char ** argv
){
2445 fprintf( stdout
, "Can't scan without a channel.\n\n");
2449 while ( EOF
!= ( c
= getopt( argc
, argv
, "123cqatxyb:w:i:r:s:l:" ) ) )
2456 if ( 0 == arg_vapi
) {
2457 fprintf( stderr
, "Invalid Video API: use 1 2 or 3\n");
2476 // signal percent threshold before audio output
2478 if (NULL
!= optarg
) {
2479 sscanf( optarg
,"%d",&arg_athr
);
2489 if (NULL
!= optarg
) {
2490 sscanf( optarg
, "%d", &arg_xdim
);
2492 vw
.height
= arg_xdim
;
2493 vw
.width
= arg_xdim
;
2497 if (NULL
!= optarg
) {
2498 sscanf( optarg
, "%d", &arg_idev
);
2503 if (NULL
!= optarg
) {
2504 sscanf( optarg
, "%d", &arg_loop
);
2509 if (NULL
!= optarg
) {
2510 sscanf( optarg
, "%d", &arg_time
);
2511 if (0 != arg_time
) sig_sleep
.tv_nsec
= arg_time
;
2517 if (NULL
!= optarg
) {
2518 sscanf( optarg
, "%d", &arg_modu
);
2519 /* arg_modu = 3.1415926 * arg_modu; */
2539 memset( in_sname
, 0, sizeof( in_sname
) );
2543 snprintf( in_sname
, sizeof( in_sname
), "dtv%d", arg_idev
);
2548 snprintf( in_sname
, sizeof( in_sname
), "dtv%d", arg_idev
);
2553 snprintf( in_sname
, sizeof( in_sname
), "dvb%d", arg_idev
);
2557 fprintf(stderr
, "\nNO VIDEO API CHOSEN\n");
2562 fprintf( stdout
, "Using %s API on %s\n", t
, in_sname
);
2566 if (0 == (argc
- k
)) {
2568 fprintf( stderr
, "Can't scan without a channel.\n\n");
2572 // fprintf( stdout, "argc - k = %d\n", argc-k);
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 */
2583 for (i
=0; i
< 81; i
++) {
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 );
2601 if (chan
< 0) chan
= 0;
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
) {
2620 // if X not started, fall back to text mode
2621 if ( NULL
== getenv( "DISPLAY" ) ) {
2626 if (0 != arg_ox11
) {
2628 x_redraw_display(1);
2633 fprintf( stderr
, "\n" );
2635 if (0 != arg_ox11
) {
2639 if (0 != arg_adsp
) {