Subversion Repositories svnkaklik

Rev

Rev 261 | Rev 334 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log

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