Subversion Repositories svnkaklik

Rev

Rev 409 | Details | Compare with Previous | Last modification | View Log

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