8magsvn – Blame information for rev 31

Subversion Repositories:
Rev:
Rev Author Line No. Line
31 kaklik 1 //
2 // sidd.c: A VLF signal monitor.
3 //
4 // authors: Paul Nicholson (paul@abelian.demon.co.uk), Jakub kakona (kaklik@mlab.cz)
5 //
6  
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <math.h>
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <sys/param.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <stdarg.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <time.h>
22 #include <sched.h>
23 #include <linux/soundcard.h>
24  
25 #include <fftw3.h>
26  
27 ///////////////////////////////////////////////////////////////////////////////
28 // Tuneable Settings //
29 ///////////////////////////////////////////////////////////////////////////////
30  
31 #define VERSION "$Id: sidd.c 20 2008-09-18 14:57:58Z kaklik $"
32  
33 //
34 // Number of soundcard bytes to read at a time.
35 #define NREAD 2048
36  
37 //
38 // Max number of bands which can be read from the config file.
39 #define MAXBANDS 20
40  
41 //
42 // Name of the configuration file.
43 #define CONFIG_FILE "/etc/sidd.conf"
44  
45 ///////////////////////////////////////////////////////////////////////////////
46 // Globals and fixed definitions //
47 ///////////////////////////////////////////////////////////////////////////////
48 //
49 // Default values here are over-ridden by the config file.
50  
51 int counter = 0; // Pocitadlo pro decimaci
52 double decim_e[MAXBANDS][2]; // Vysledky Decimace pro jednotlive frekvence, stereo
53 double fsec_old = 0; // Posledni cas vypisu
54 struct STATION
55 {
56 char name[30];
57 char lat[20];
58 char lon[20];
59 }
60 station; // Station parameters
61  
62 int mode = 1; // 1 = mono, 2 = stereo
63 int bits = 16; // Sample width, 8 or 16 bits
64 int BINS = 2048; // Number of frequency bins
65 #define FFTWID (2 * BINS) // Number of samples in FFT period
66  
67 int background = 1; // Set zero if running in foreground
68 int fdi; // Input file handle
69 int fdm; // Mixer file handle
70 int VFLAG = 0; // Set non-zero by -v option
71 int MFLAG = 0; // Set non-zero by -m option
72  
73 int spec_max = 100; // Issue a spectrum for every spec_max output records
74 int spec_cnt = 0;
75 int sample_rate = 100000; // Samples per second
76  
77 int chans = 1;
78 int alert_on = 0;
79  
80 int priority = 0; // Set to 1 if high scheduling priority
81 struct sigaction sa;
82 char mailaddr[100];
83  
84 double los_thresh = 0; // Threshold for loss of signal, 0..1
85 int los_timeout = 0; // Number of seconds before loss of signal declared
86  
87 double DF; // Frequency resolution of the FFT
88 int bailout_flag = 0; // To prevent bailout() looping
89 int grab_cnt = 0; // Count of samples into the FFT buffer
90  
91 // Mixer gain settings requested by config file.
92 int req_lgain = -1; // Line gain
93 int req_mgain = -1; // Microphone gain
94 int req_igain = -1; // Input gain
95 int req_rgain = -1; // Record level
96  
97  
98 // Actual mixer values, read by open_mixer()
99 int mixer_recmask; // Recording device mask
100 int mixer_stereo; // Stereo device mask
101 int mixer_line; // Line input gain setting
102 int mixer_microphone; // Microphone input gain
103 int mixer_igain; // Overall input gain setting
104 int mixer_reclev; // Recording level setting
105 int mixer_recsrc; // Mask indicating which inputs are set to record
106  
107 //
108 // Various filenames, contents set by config file.
109 //
110 char logfile[100] = "";
111 char device[100] = "/dev/dsp";
112 char mixer[100] = "/dev/mixer";
113 char spectrum_file[100] = "/tmp/sidspec";
114 char datadir[100] = ".";
115  
116 //
117 // Table of frequency bands to monitor
118 //
119  
120 struct BAND
121 {
122 char ident[50];
123  
124 int start;
125 int end;
126 }
127 bands[MAXBANDS]; // Table of bands to be monitored
128  
129 int nbands = 0;
130  
131 //
132 // Independent state variables and buffers for left and right channels
133 //
134 struct CHAN
135 {
136 char *name;
137 double *signal_avg;
138 double *powspec;
139 double *fft_inbuf;
140 fftw_complex *fft_data;
141 fftw_plan ffp;
142 double peak;
143 double sum_sq;
144 int los_state;
145 time_t los_time;
146 FILE *fo;
147 char fname[100];
148 }
149 left = { "left" }, right = { "right" };
150  
151 ///////////////////////////////////////////////////////////////////////////////
152 // Various Utility Functions //
153 ///////////////////////////////////////////////////////////////////////////////
154  
155 //
156 // Issue a message to the log file, if the verbosity level is high enough...
157 //
158  
159 void report( int level, char *format, ...)
160 {
161 va_list ap;
162 void bailout( char *format, ...);
163 char temp[ 200];
164  
165 if( VFLAG < level) return;
166  
167 va_start( ap, format);
168 vsprintf( temp, format, ap);
169 va_end( ap);
170  
171 if( !logfile[0] || !background)
172 if( background != 2) fprintf( stderr, "%s\n", temp);
173  
174 if( logfile[0])
175 {
176 time_t now = time( NULL);
177 struct tm *tm = gmtime( &now);
178 FILE *flog = NULL;
179  
180 if( (flog = fopen( logfile, "a+")) == NULL)
181 bailout( "cannot open logfile [%s]: %s", logfile, strerror( errno));
182  
183 fprintf( flog, "%04d/%02d/%02d %02d:%02d:%02d %s\n",
184 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
185 tm->tm_hour, tm->tm_min, tm->tm_sec, temp);
186 fclose( flog);
187 }
188 }
189  
190 void alert( char *format, ...)
191 {
192 FILE *f;
193 va_list( ap);
194 char cmd[100], temp[100];
195  
196 va_start( ap, format);
197 vsprintf( temp, format, ap);
198 va_end( ap);
199  
200 report( -1, "%s", temp);
201  
202 if( !alert_on || !mailaddr[0]) return;
203  
204 sprintf( cmd, "mail -s 'sidd alert' '%s'", mailaddr);
205 if( (f=popen( cmd, "w")) == NULL)
206 {
207 report( 0, "cannot exec [%s]: %s", cmd, strerror( errno));
208 return;
209 }
210  
211 fprintf( f, "sidd: %s\n", temp);
212 fclose( f);
213 }
214  
215 //
216 // We try to exit the program through here, if possible.
217 //
218  
219 void bailout( char *format, ...)
220 {
221 va_list ap;
222 char temp[ 200];
223  
224 if( bailout_flag) exit( 1);
225 bailout_flag = 1;
226 va_start( ap, format);
227 vsprintf( temp, format, ap);
228 va_end( ap);
229  
230 alert( "terminating: %s", temp);
231 exit( 1);
232 }
233  
234 //
235 // Exit with a message if we get any signals.
236 //
237  
238 void handle_sigs( int signum)
239 {
240 bailout( "got signal %d", signum);
241 }
242  
243 ///////////////////////////////////////////////////////////////////////////////
244 // Soundcard Setup //
245 ///////////////////////////////////////////////////////////////////////////////
246  
247 //
248 // Prepare the input stream, setting up the soundcard if the input
249 // is a character device.
250 //
251  
252 void setup_input_stream( void)
253 {
254 struct stat st;
255  
256 report( 1, "taking data from [%s]", device);
257  
258 if( (fdi = open( device, O_RDONLY)) < 0)
259 bailout( "cannot open [%s]: %s", strerror( errno));
260  
261 if( fstat( fdi, &st) < 0)
262 bailout( "cannot stat input stream: %s", strerror( errno));
263  
264 if( S_ISCHR( st.st_mode))
265 {
266 int blksize;
267 int fragreq = 0x7fff000a;
268 unsigned int format;
269 unsigned int req_format = AFMT_S16_LE;
270 if( bits == 8) req_format = AFMT_U8;
271  
272 if (ioctl( fdi, SNDCTL_DSP_SETFRAGMENT, &fragreq))
273 report( 01, "cannot set fragment size");
274  
275 if( ioctl( fdi, SNDCTL_DSP_RESET, NULL) < 0)
276 bailout( "cannot reset input device");
277  
278 chans = mode;
279 if( ioctl( fdi, SNDCTL_DSP_CHANNELS, &chans) < 0)
280 bailout( "cannot set channels on input device");
281  
282 if( ioctl( fdi, SNDCTL_DSP_GETFMTS, &format) < 0)
283 bailout( "cannot get formats from input device");
284  
285 report( 2, "formats available: %08X", format);
286 if( (format & req_format) == 0)
287 {
288 report( 0, "available dsp modes are %08X", format);
289 bailout( "unable to set %d bit dsp mode", bits);
290 }
291  
292 format = req_format;
293 if( ioctl( fdi, SNDCTL_DSP_SETFMT, &format) < 0)
294 bailout( "cannot set dsp format on input device");
295  
296 if( ioctl( fdi, SNDCTL_DSP_GETBLKSIZE, &blksize) < 0)
297 bailout( "cannot get block size from input device");
298  
299 report( 2, "dsp block size: %d", blksize);
300 if( ioctl( fdi, SNDCTL_DSP_CHANNELS, &chans) < 0)
301 bailout( "cannot get channels from input device");
302  
303 report( 1, "requesting rate %d", sample_rate);
304 if( ioctl( fdi, SNDCTL_DSP_SPEED, &sample_rate) < 0)
305 bailout( "cannot set sample rate of input device");
306  
307 report( 1, "actual rate set: %d samples/sec", sample_rate);
308 report( 1, "soundcard channels: %d bits: %d", chans, bits);
309 }
310 }
311  
312 ///////////////////////////////////////////////////////////////////////////////
313 // Output Functions //
314 ///////////////////////////////////////////////////////////////////////////////
315  
316 void maybe_output_spectrum( void)
317 {
318 FILE *f;
319 int i;
320  
321 if( ++spec_cnt < spec_max) return; // Wait for spec_max records
322 spec_cnt = 0;
323  
324 if( !spectrum_file[0]) return; // Spectrum file not wanted.
325  
326 if( (f=fopen( spectrum_file, "w+")) == NULL)
327 bailout( "cannot open spectrum file %s, %s", strerror( errno));
328  
329 if( mode == 1){
330 fprintf( f, "Frequency PowerL \n");
331 for( i=0; i<BINS; i++) fprintf( f, "%.5e %.5e\n",
332 (i+0.5) * DF, left.signal_avg[i]/spec_max);}
333 else{
334 fprintf( f, "Frequncy PowerL PowerR \n");
335 for( i=0; i<BINS; i++) fprintf( f, "%.5e %.5e %.5e\n",
336 (i+0.5) * DF, left.signal_avg[i]/spec_max,
337 right.signal_avg[i]/spec_max);}
338 fclose( f);
339  
340 for( i=0; i<BINS; i++) left.signal_avg[i] = 0;
341 if( mode == 2) for( i=0; i<BINS; i++) right.signal_avg[i] = 0;
342 }
343  
344 void output_record(int channel, struct CHAN *c, char *prefix, double fsecs)
345 {
346 int i, j;
347 char test[100];
348 int nn;
349  
350 counter++;
351 for( i=0; i<nbands; i++)
352 {
353 double e = 0;
354 int n1 = bands[i].start/DF;
355 int n2 = bands[i].end/DF;
356 for( j=n1; j<= n2; j++) e += c->powspec[j];
357 e /= n2 - n1 + 1;
358 decim_e[i][channel]+=e; // Nacitame hodnoty do decimacni promenne
359 c->peak = c->sum_sq = 0; // Nulujeme kazdy beh
360 }
361  
362 if (fsecs>=(fsec_old+1)) // Ubehla jedna sekunda od posledniho mereni?
363 {
364 if( mode == 1)
365 {
366 fsec_old=fsecs; // Poznamename cas
367 sprintf( test, "%s.dat", prefix);
368 }
369 else
370 {
371 sprintf( test, "%s.%s.dat", prefix, c->name);
372 if (channel==1) fsec_old=fsecs; // Poznamename cas
373 }
374  
375 if( !c->fo || strcmp( test, c->fname))
376 {
377 if( c->fo) fclose( c->fo);
378 strcpy( c->fname, test);
379 report( 0, "using output file [%s]", c->fname);
380 if( (c->fo=fopen( c->fname, "a+")) == NULL)
381 bailout( "cannot open [%s], %s", c->fname, strerror( errno));
382 fprintf( c->fo, "%s ", station.name);
383 fprintf( c->fo, "%s ", station.lat);
384 fprintf( c->fo, "%s ", station.lon);
385 for(nn=0; nn<=nbands; nn++)
386 {
387 fprintf( c->fo, "%s ", bands[nn].ident);
388 };
389 fprintf( c->fo, "\n");
390 }
391  
392 fprintf( c->fo, "%.3f %.3f %.3f", fsecs, c->peak, sqrt( c->sum_sq/FFTWID));
393  
394 for( i=0; i<nbands; i++)
395 {
396 fprintf( c->fo, " %.2e", decim_e[i][channel]/counter); // Aritmeticky prumer z nactenych hodnot
397 decim_e[i][channel]=0; // Vynulujeme promennou pro prumerovani
398 }
399 fprintf( c->fo, "\n");
400 fflush( c->fo);
401 counter=0; // Vynulujeme pocitadlo decimace
402 }
403 }
404  
405  
406 void output_records( void)
407 {
408 struct timeval tv;
409 struct tm *tm;
410 double fsecs;
411 time_t ud;
412 char prefix[100];
413  
414 gettimeofday( &tv, NULL);
415 fsecs = tv.tv_sec + 1e-6 * tv.tv_usec;
416 ud = tv.tv_sec - tv.tv_sec % 86400;
417 tm = gmtime( &ud);
418 sprintf( prefix, "%s/%02d%02d%02d", datadir,
419 tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday);
420  
421 output_record(0, &left, prefix, fsecs);
422 if( mode == 2) output_record(1, &right, prefix, fsecs);
423 }
424  
425 void check_los( struct CHAN *c)
426 {
427 if( !c->los_state)
428 {
429 if( !c->los_time && c->peak < los_thresh) time( &c->los_time);
430 if( c->los_time && c->peak > los_thresh) c->los_time = 0;
431 if( c->los_time && c->los_time + los_timeout < time( NULL))
432 {
433 c->los_state = 1;
434 c->los_time = 0;
435 if( mode == 1) alert( "loss of signal");
436 else alert( "loss of signal on %s", c->name);
437 }
438 }
439 else
440 {
441 if( !c->los_time && c->peak > los_thresh) time( &c->los_time);
442 if( c->los_time && c->peak < los_thresh) c->los_time = 0;
443 if( c->los_time && c->los_time + los_timeout < time( NULL))
444 {
445 c->los_state = 0;
446 c->los_time = 0;
447 if( mode == 1) alert( "signal restored");
448 else alert( "signal restored on %s", c->name);
449 }
450 }
451 }
452  
453 ///////////////////////////////////////////////////////////////////////////////
454 // Signal Processing //
455 ///////////////////////////////////////////////////////////////////////////////
456  
457 void process_fft( struct CHAN *c)
458 {
459 int i;
460  
461 //
462 // Do the FFT. First time through, initialise the fft plan.
463 //
464  
465 if( !c->ffp)
466 c->ffp = fftw_plan_dft_r2c_1d( FFTWID, c->fft_inbuf, c->fft_data,
467 FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
468  
469 fftw_execute( c->ffp);
470  
471 //
472 // Obtain squared amplitude of each bin.
473 //
474  
475 c->powspec[ 0] = 0.0; // Zero the DC component
476 for( i=1; i<BINS; i++)
477 {
478 double t1 = c->fft_data[ i][0];
479 double t2 = c->fft_data[ i][1];
480 c->powspec[ i] = t1*t1 + t2*t2;
481 }
482  
483 //
484 // Accumulate average signal levels in each bin. signal_avg is used
485 // only for the spectrum file output.
486 //
487  
488 for( i=0; i<BINS; i++) c->signal_avg[ i] += c->powspec[i];
489 check_los( c);
490 }
491  
492 void insert_sample( struct CHAN *c, double f)
493 {
494 c->sum_sq += f * f;
495 if( f > c->peak) c->peak = f;
496 if( f < -c->peak) c->peak = -f;
497  
498 c->fft_inbuf[ grab_cnt] = f * sin( grab_cnt/(double) FFTWID * M_PI);
499 }
500  
501 void maybe_do_fft( void)
502 {
503 if( ++grab_cnt < FFTWID) return;
504 grab_cnt = 0;
505  
506 process_fft( &left);
507 if( mode == 2) process_fft( &right);
508  
509 output_records();
510 maybe_output_spectrum();
511 }
512  
513 //
514 // Main signal processing loop. Never returns.
515 //
516  
517 void process_signal( void)
518 {
519 unsigned char buff[ NREAD];
520  
521 while( 1)
522 {
523 int i, q;
524  
525 if( (q=read( fdi, buff, NREAD)) <= 0)
526 {
527 if( !q || errno == ENOENT || errno == 0)
528 {
529 sched_yield();
530 usleep( 50000);
531 continue;
532 }
533  
534 report( 0, "input file: read error, count=%d errno=%d", q, errno);
535 exit( 1);
536 }
537  
538 // Unpack the input buffer into signed 16 bit words.
539 // then scale to -1..+1 for further processing.
540 // We use 'chans' to decide if the soundcard is giving stereo or
541 // mono samples, rather than 'mode', because some cards will refuse
542 // to do mono.
543 if( bits == 16)
544 {
545 if( chans == 1)
546 {
547 for( i=0; i<q; i += 2)
548 {
549 int fh = *(short *)(buff + i);
550  
551 insert_sample( &left, fh/32768.0);
552 maybe_do_fft();
553 }
554 }
555 else // chans must be 2
556 {
557 if( mode == 1)
558 for( i=0; i<q; i += 4)
559 {
560 int fh = *(short *)(buff + i);
561 insert_sample( &left, fh/32768.0);
562 maybe_do_fft();
563 }
564 else // mode == 2
565 for( i=0; i<q; i += 4)
566 {
567 int fh = *(short *)(buff + i);
568 insert_sample( &left, fh/32768.0);
569  
570 fh = *(short *)(buff + i + 2);
571 insert_sample( &right, fh/32768.0);
572 maybe_do_fft();
573 }
574 }
575 }
576 else // bits must be 8
577 {
578 if( chans == 1)
579 {
580 for( i=0; i<q; i++)
581 {
582 int fh = ((short)buff[i] - 128)*256;
583 insert_sample( &left, fh/32768.0);
584 maybe_do_fft();
585 }
586 }
587 else // chans must be 2
588 {
589 if( mode == 1)
590 for( i=0; i<q; i += 2)
591 {
592 int fh = ((short)buff[i] - 128)*256;
593 insert_sample( &left, fh/32768.0);
594 maybe_do_fft();
595 }
596 else // mode == 2
597 for( i=0; i<q; i += 2)
598 {
599 int fh = ((short)buff[i] - 128)*256;
600 insert_sample( &left, fh/32768.0);
601  
602 fh = ((short)buff[i+1] - 128)*256;
603 insert_sample( &right, fh/32768.0);
604 maybe_do_fft();
605 }
606 }
607 }
608 }
609 }
610  
611 ///////////////////////////////////////////////////////////////////////////////
612 // Configuration File Stuff //
613 ///////////////////////////////////////////////////////////////////////////////
614  
615 void config_band( char *ident, char *start, char *end)
616 {
617 struct BAND *b = bands + nbands++;
618  
619 if( nbands == MAXBANDS) bailout( "too many bands specified in config file");
620  
621 strcpy( b->ident, ident);
622 b->start = atoi( start);
623 b->end = atoi( end);
624  
625 report( 1, "band %s %d %d", b->ident, b->start, b->end);
626 }
627  
628 void load_config( void)
629 {
630 int lino = 0, nf;
631 FILE *f;
632 char buff[100], *p, *fields[20];
633  
634 if( (f=fopen( CONFIG_FILE, "r")) == NULL)
635 bailout( "no config file found");
636  
637 while( fgets( buff, 99, f))
638 {
639 lino++;
640  
641 if( (p=strchr( buff, '\r')) != NULL) *p = 0;
642 if( (p=strchr( buff, '\n')) != NULL) *p = 0;
643 if( (p=strchr( buff, ';')) != NULL) *p = 0;
644  
645 p = buff; nf = 0;
646 while( 1)
647 {
648 while( *p && isspace( *p)) p++;
649 if( !*p) break;
650 fields[nf++] = p;
651 while( *p && !isspace( *p)) p++;
652 if( *p) *p++ = 0;
653 }
654 if( !nf) continue;
655  
656 if( nf == 4 && !strcasecmp( fields[0], "station"))
657 {
658 strcpy( station.name, fields[1]);
659 strcpy( station.lat, fields[2]);
660 strcpy( station.lon, fields[3]);
661 }
662 else
663 if( nf == 4 && !strcasecmp( fields[0], "band"))
664 config_band( fields[1], fields[2], fields[3]);
665 else
666 if( nf == 2 && !strcasecmp( fields[0], "logfile"))
667 {
668 strcpy( logfile, fields[1]);
669 report( 1, "logfile %s", logfile);
670 }
671 else
672 if( nf == 3 && !strcasecmp( fields[0], "los"))
673 {
674 los_thresh = atof( fields[1]);
675 los_timeout = atoi( fields[2]);
676 report( 1, "los threshold %.3f, timeout %d seconds",
677 los_thresh, los_timeout);
678 }
679 else
680 if( nf == 2 && !strcasecmp( fields[0], "device"))
681 strcpy( device, fields[1]);
682 else
683 if( nf == 2 && !strcasecmp( fields[0], "mixer"))
684 strcpy( mixer, fields[1]);
685 else
686 if( nf == 2 && !strcasecmp( fields[0], "mode"))
687 {
688 if( !strcasecmp( fields[1], "mono")) mode = 1;
689 else
690 if( !strcasecmp( fields[1], "stereo")) mode = 2;
691 else
692 bailout( "error in config file, line %d", lino);
693 }
694 else
695 if( nf == 2 && !strcasecmp( fields[0], "bits"))
696 {
697 bits = atoi( fields[1]);
698 if( bits != 8 && bits != 16)
699 bailout( "can only do 8 or 16 bits, config file line %d", lino);
700 }
701 else
702 if( nf == 3 && !strcasecmp( fields[0], "spectrum"))
703 {
704 strcpy( spectrum_file, fields[1]);
705 spec_max = atoi( fields[2]);
706 }
707 else
708 if( nf == 2 && !strcasecmp( fields[0], "sched")
709 && !strcasecmp( fields[1], "high"))
710 {
711 priority = 1;
712 }
713 else
714 if( nf == 4 && !strcasecmp( fields[0], "gain"))
715 {
716 int left = atoi( fields[2]);
717 int right = atoi( fields[3]);
718 int gain = (right << 8) | left;
719  
720 if( !strcasecmp( fields[1], "line"))
721 {
722 req_lgain = gain;
723 mixer_recsrc = SOUND_MASK_LINE;
724 }
725 else
726 if( !strcasecmp( fields[1], "mic"))
727 {
728 req_mgain = gain;
729 mixer_recsrc = SOUND_MASK_MIC;
730 }
731 else
732 if( !strcasecmp( fields[1], "overall")) req_igain = gain;
733 else
734 if( !strcasecmp( fields[1], "record")) req_rgain = gain;
735 else
736 bailout( "unknown gain control [%s]", fields[1]);
737 }
738 else
739 if( nf == 2 && !strcasecmp( fields[0], "rate"))
740 sample_rate = atoi( fields[1]);
741 else
742 if( nf == 2 && !strcasecmp( fields[0], "bins"))
743 BINS = atoi( fields[1]);
744 else
745 if( nf == 2 && !strcasecmp( fields[0], "datadir"))
746 {
747 struct stat st;
748 strcpy( datadir, fields[1]);
749 if( stat( datadir, &st) < 0 || !S_ISDIR( st.st_mode))
750 bailout( "no data directory, %s", datadir);
751 }
752 else
753 bailout( "error in config file, line %d", lino);
754 }
755  
756 fclose( f);
757 }
758  
759 ///////////////////////////////////////////////////////////////////////////////
760 // Mixer Stuff //
761 ///////////////////////////////////////////////////////////////////////////////
762  
763 void open_mixer( void)
764 {
765 if( (fdm = open( mixer, O_RDWR)) < 0)
766 bailout( "cannot open [%s]: %s", mixer, strerror( errno));
767  
768 // Determine the available mixer recording gain controls.
769 //! We must at least have a line input.
770  
771 if( ioctl( fdm, SOUND_MIXER_READ_RECMASK, &mixer_recmask) < 0)
772 bailout( "cannot read mixer devmask");
773  
774 // if( (mixer_recmask & SOUND_MASK_LINE) == 0)
775 //! bailout( "mixer has no line device");
776  
777 if( ioctl( fdm, SOUND_MIXER_READ_STEREODEVS, &mixer_stereo) < 0)
778 bailout( "cannot read mixer stereodevs");
779  
780 if( ioctl( fdm, SOUND_MIXER_READ_RECSRC, &mixer_recsrc) < 0)
781 bailout( "cannot read mixer recsrc");
782  
783 // Read the line input gain.
784 // if( ioctl( fdm, SOUND_MIXER_READ_LINE, &mixer_line) < 0)
785 //! bailout( "cannot read mixer line");
786  
787 // Read overall input gain? Optional.
788 if( (mixer_recmask & SOUND_MASK_IGAIN) &&
789 ioctl( fdm, SOUND_MIXER_READ_IGAIN, &mixer_igain) < 0)
790 bailout( "cannot read mixer igain");
791  
792 // Read overall recording level? Optional.
793 if( (mixer_recmask & SOUND_MASK_RECLEV) &&
794 ioctl( fdm, SOUND_MIXER_READ_RECLEV, &mixer_reclev) < 0)
795 bailout( "cannot read mixer reclev");
796 }
797  
798 void report_mixer_settings( void)
799 {
800 report( 1, "mixer: line input is %s",
801 mixer_stereo & SOUND_MASK_LINE ? "stereo" : "mono");
802  
803 report( 1, "mixer: line input is %s",
804 mixer_recsrc & SOUND_MASK_LINE ? "on" : "off");
805  
806 report( 1, "mixer: line input gain: left=%d right=%d",
807 mixer_line & 0xff, (mixer_line >> 8) & 0xff);
808  
809 // Overall input gain? Optional.
810 if( mixer_recmask & SOUND_MASK_IGAIN)
811 {
812 report( 1, "mixer: igain: left=%d right=%d",
813 mixer_igain & 0xff, (mixer_igain >> 8) & 0xff);
814 }
815 else report( 1, "mixer: igain: n/a");
816  
817 // Overall recording level? Optional.
818 if( mixer_recmask & SOUND_MASK_RECLEV)
819 {
820 report( 1, "mixer: reclev: left=%d right=%d",
821 mixer_reclev & 0xff, (mixer_reclev >> 8) & 0xff);
822 }
823 else report( 1, "mixer: reclev: n/a");
824  
825 }
826  
827 void setup_mixer( void)
828 {
829 if( req_lgain >= 0)
830 {
831 report( 1, "requesting line input gains left=%d right=%d",
832 req_lgain & 0xff, (req_lgain >> 8) & 0xff);
833  
834 if( ioctl( fdm, SOUND_MIXER_WRITE_LINE, &req_lgain) < 0 ||
835 ioctl( fdm, SOUND_MIXER_READ_LINE, &mixer_line) < 0)
836 bailout( "error setting mixer line gain");
837  
838 report( 1, "line input gains set to: left=%d right=%d",
839 mixer_line & 0xff, (mixer_line >> 8) & 0xff);
840 }
841  
842 if( req_mgain >= 0)
843 {
844 report( 1, "requesting microphone input gains left=%d right=%d",
845 req_mgain & 0xff, (req_mgain >> 8) & 0xff);
846  
847 if( ioctl( fdm, SOUND_MIXER_WRITE_MIC, &req_mgain) < 0 ||
848 ioctl( fdm, SOUND_MIXER_READ_MIC, &mixer_microphone) < 0)
849 bailout( "error setting mixer microphone gain");
850  
851 report( 1, "Microphone input gains set to: left=%d right=%d",
852 mixer_microphone & 0xff, (mixer_microphone >> 8) & 0xff);
853 }
854  
855 if( req_igain >= 0 &&
856 (mixer_recmask & SOUND_MASK_IGAIN))
857 {
858 report( 1, "requesting overall input gains left=%d right=%d",
859 req_igain & 0xff, (req_igain >> 8) & 0xff);
860  
861 if( ioctl( fdm, SOUND_MIXER_WRITE_IGAIN, &req_igain) < 0 ||
862 ioctl( fdm, SOUND_MIXER_READ_IGAIN, &mixer_igain) < 0)
863 bailout( "error setting mixer overall input gain");
864  
865 report( 1, "overall input gains set to: left=%d right=%d",
866 mixer_igain & 0xff, (mixer_igain >> 8) & 0xff);
867 }
868  
869 if( req_rgain >= 0 &&
870 (mixer_recmask & SOUND_MASK_RECLEV))
871 {
872 report( 1, "requesting overall record levels left=%d right=%d",
873 req_rgain & 0xff, (req_rgain >> 8) & 0xff);
874  
875 if( ioctl( fdm, SOUND_MIXER_WRITE_RECLEV, &req_rgain) < 0 ||
876 ioctl( fdm, SOUND_MIXER_READ_RECLEV, &mixer_reclev) < 0)
877 bailout( "error setting mixer record level");
878  
879 report( 1, "mixer record levels set to: left=%d right=%d",
880 mixer_reclev & 0xff, (mixer_reclev >> 8) & 0xff);
881 }
882  
883 //!!!mixer_recsrc= SOUND_MASK_LINE;
884 mixer_recsrc= SOUND_MASK_MIC;
885  
886 switch (mixer_recsrc)
887 {
888 case SOUND_MASK_MIC:
889  
890 if( ioctl( fdm, SOUND_MIXER_WRITE_RECSRC, &mixer_recsrc) < 0)
891 bailout( "cannot set mixer recsrc to microphone");
892 else report(1, "Input device set to microphone");
893 break;
894  
895 case SOUND_MASK_LINE:
896 if( ioctl( fdm, SOUND_MIXER_WRITE_RECSRC, &mixer_recsrc) < 0)
897 bailout( "cannot set mixer recsrc to line");
898 else report(1, "Input device set to line");
899 break;
900 }
901 }
902  
903 ///////////////////////////////////////////////////////////////////////////////
904 // Main //
905 ///////////////////////////////////////////////////////////////////////////////
906  
907 void make_daemon( void)
908 {
909 int childpid, fd;
910  
911 if( (childpid = fork()) < 0)
912 bailout( "cannot fork: %s", strerror( errno));
913 else if( childpid > 0) exit( 0);
914  
915 if( setpgrp() == -1) bailout( "cannot setpgrp");
916  
917 if( (childpid = fork()) < 0)
918 bailout( "cannot fork: %s", strerror( errno));
919 else if( childpid > 0) exit( 0);
920  
921 for( fd = 0; fd <NOFILE; fd++) if( fd != fdi) close( fd);
922 errno = 0;
923 background = 2;
924 }
925  
926 void initialise_channel( struct CHAN *c)
927 {
928 int i;
929  
930 c->fft_inbuf = (double *) malloc( BINS * 2 * sizeof( double));
931 c->fft_data = fftw_malloc( sizeof( fftw_complex) * FFTWID);
932 c->powspec = (double *) malloc( BINS * sizeof( double));
933 c->signal_avg = (double *) malloc( BINS * sizeof( double));
934 for( i=0; i<BINS; i++) c->signal_avg[i] = 0;
935 }
936  
937 void setup_signal_handling( void)
938 {
939 sa.sa_handler = handle_sigs;
940 sigemptyset( &sa.sa_mask);
941 sa.sa_flags = 0;
942 sigaction( SIGINT, &sa, NULL);
943 sigaction( SIGTERM, &sa, NULL);
944 sigaction( SIGHUP, &sa, NULL);
945 sigaction( SIGQUIT, &sa, NULL);
946 sigaction( SIGFPE, &sa, NULL);
947 sigaction( SIGBUS, &sa, NULL);
948 sigaction( SIGSEGV, &sa, NULL);
949 }
950  
951 // Set scheduling priority to the minimum SCHED_FIFO value.
952 void set_scheduling( void)
953 {
954 struct sched_param pa;
955 int min = sched_get_priority_min( SCHED_FIFO);
956  
957 pa.sched_priority = min;
958 if( sched_setscheduler( 0, SCHED_FIFO, &pa) < 0)
959 report( -1, "cannot set scheduling priority: %s", strerror( errno));
960 else
961 report( 0, "using SCHED_FIFO priority %d", min);
962 }
963  
964 int main( int argc, char *argv[])
965 {
966 while( 1)
967 {
968 int c = getopt( argc, argv, "vfm");
969  
970 if( c == 'v') VFLAG++;
971 else
972 if( c == 'm') MFLAG++;
973 else
974 if( c == 'f') background = 0;
975 else if( c == -1) break;
976 else bailout( "unknown option [%c]", c);
977 }
978  
979 setup_signal_handling();
980 load_config();
981 open_mixer();
982  
983 if( MFLAG)
984 {
985 VFLAG = 1;
986 background = 0;
987 report_mixer_settings();
988 exit( 0);
989 }
990  
991 setup_mixer();
992  
993 if( background && !logfile[0])
994 report( -1, "warning: no logfile specified for daemon");
995  
996 setup_input_stream();
997 DF = (double) sample_rate/(double) FFTWID;
998  
999 report( 1, "resolution: bins=%d fftwid=%d df=%f", BINS, FFTWID, DF);
1000 report( 1, "spectrum file: %s", spectrum_file);
1001  
1002 initialise_channel( &left);
1003 if( mode == 2) initialise_channel( &right);
1004  
1005 if( background) make_daemon();
1006 if( priority) set_scheduling();
1007  
1008 report( 0, "sidd version %s: starting work", VERSION);
1009 alert_on = 1;
1010 process_signal();
1011 return 0;
1012 }
1013