Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
spec_avg.c
Go to the documentation of this file.
1/*
2* Copyright (C) 2007 Gregory Mendell
3* 2021-2025 Evan Goetz
4*
5* This program is free software; you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation; either version 2 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with with program; see the file COPYING. If not, write to the
17* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18* MA 02110-1301 USA
19*/
20
21/**
22 * \file
23 * \ingroup lalpulsar_bin_SFTTools
24 */
25
26#include <libgen.h>
27#include <unistd.h>
28#include <lal/LogPrintf.h>
29#include <lal/UserInput.h>
30#include <lal/FrequencySeries.h>
31#include <lal/SFTfileIO.h>
32#include <lal/NormalizeSFTRngMed.h>
33#include <lal/LALPulsarVCSInfo.h>
34
35#include "fscanutils.h"
36
37int main( int argc, char **argv )
38{
39 lalUserVarHelpBrief = "Normalize and average SFTs and compute spectrogram from SFTs for Fscan";
40 lalUserVarHelpDescription = "Provide SFTs to this program for computing normalized and averaged spectra and spectrogram saved as ASCII data files for use by Fscan";
41
42 FILE *fp = NULL, *fp2 = NULL, *fp3 = NULL, *fp4 = NULL;
43 int fopenerr = 0;
44
45 SFTCatalog *catalog = NULL;
46 SFTVector *sft_vect = NULL;
47 UINT4 NumBinsAvg = 1;
48 SFTConstraints XLAL_INIT_DECL( constraints );
50 LIGOTimeGPS XLAL_INIT_DECL( endTime );
51 REAL4Vector *timeavg = NULL;
52 CHAR outbase[256], outfile[512], outfile2[512], outfile3[512], outfile4[512];
53
54 CHAR *SFTpatt = NULL, *IFO = NULL, *outputDir = NULL, *outputBname = NULL, *header = NULL;
55 INT4 startGPS = 0, endGPS = 0;
56 REAL8 f_min = 0.0, f_max = 0.0, freqres = 0.1, subband = 100.0, timebaseline = 0;
57 INT4 blocksRngMed = 101, cur_epoch = 0;
58
59 /* these varibales are for converting GPS seconds into UTC time and date*/
60 struct tm date;
61 INT4Vector *timestamps = NULL;
62
63 /* Default for output directory */
64 XLAL_CHECK_MAIN( ( outputDir = XLALStringDuplicate( "." ) ) != NULL, XLAL_EFUNC );
65
66 BOOLEAN allow_skipping = 0;
67
68 /*========================================================================================================================*/
69
70 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &SFTpatt, "SFTs", STRING, 'p', REQUIRED, "SFT location/pattern. Possibilities are:\n"
71 " - '<SFT file>;<SFT file>;...', where <SFT file> may contain wildcards\n - 'list:<file containing list of SFT files>'" ) == XLAL_SUCCESS, XLAL_EFUNC );
72 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &IFO, "IFO", STRING, 'I', REQUIRED, "Detector" ) == XLAL_SUCCESS, XLAL_EFUNC );
73 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &startGPS, "startGPS", INT4, 's', REQUIRED, "Starting GPS time (SFT timestamps must be >= this)" ) == XLAL_SUCCESS, XLAL_EFUNC );
74 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &endGPS, "endGPS", INT4, 'e', REQUIRED, "Ending GPS time (SFT timestamps must be < this)" ) == XLAL_SUCCESS, XLAL_EFUNC );
75 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &f_min, "fMin", REAL8, 'f', REQUIRED, "Minimum frequency in Hz" ) == XLAL_SUCCESS, XLAL_EFUNC );
76 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &f_max, "fMax", REAL8, 'F', REQUIRED, "Maximum frequency in Hz" ) == XLAL_SUCCESS, XLAL_EFUNC );
77 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &blocksRngMed, "blocksRngMed", INT4, 'w', OPTIONAL, "Running Median window size" ) == XLAL_SUCCESS, XLAL_EFUNC );
78 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &outputDir, "outputDir", STRING, 'd', OPTIONAL, "Output directory for data files" ) == XLAL_SUCCESS, XLAL_EFUNC );
79 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &outputBname, "outputBname", STRING, 'o', OPTIONAL, "Base name of output files" ) == XLAL_SUCCESS, XLAL_EFUNC );
80 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &freqres, "freqRes", REAL8, 'r', OPTIONAL, "Spectrogram freq resolution in Hz" ) == XLAL_SUCCESS, XLAL_EFUNC );
81 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &subband, "subband", REAL8, 'b', OPTIONAL, "Subdivide the output normalized average spectra txt files into these subbands" ) == XLAL_SUCCESS, XLAL_EFUNC );
82 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &timebaseline, "timeBaseline", REAL8, 't', REQUIRED, "The time baseline of sfts in seconds" ) == XLAL_SUCCESS, XLAL_EFUNC );
83 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &allow_skipping, "allow_skipping", BOOLEAN, 'x', OPTIONAL, "Allow to exit without an error if no SFTs are found" ) == XLAL_SUCCESS, XLAL_EFUNC );
84 XLAL_CHECK_MAIN( XLALRegisterNamedUvar( &header, "header", STRING, 'H', OPTIONAL, "Header line in the output file; if not provided then no header line will be made" ) == XLAL_SUCCESS, XLAL_EFUNC );
85
86 BOOLEAN should_exit = 0;
88 if ( should_exit ) {
89 return ( 1 );
90 }
91
92 // Populate the startTime and endTime LIGOTimeGPS variables
93 startTime.gpsSeconds = startGPS;
94 endTime.gpsSeconds = endGPS;
95 startTime.gpsNanoSeconds = endTime.gpsNanoSeconds = 0;
96
97 // Populate the SFT catalog constraints
98 constraints.minStartTime = &startTime;
99 constraints.maxStartTime = &endTime;
100 constraints.detector = IFO;
101
102 // Load SFT catalog
103 int errnum;
104 XLAL_TRY( catalog = XLALSFTdataFind( SFTpatt, &constraints ), errnum );
105
106 // Ensure that some SFTs were found given the start and end time and IFO constraints
107 // unless --allow_skipping is true
108 if ( errnum != 0 ) {
109 if ( allow_skipping ) {
110 LogPrintf( LOG_CRITICAL, "No SFTs were found, exiting with code %d due to --allow_skipping=true\n", XLAL_EUSR0 );
112 exit( XLAL_EUSR0 );
113 } else {
114 XLAL_ERROR_MAIN( errnum );
115 }
116 }
117
118 XLAL_CHECK_MAIN( catalog->length > 0, XLAL_EFAILED, "No SFTs found, please examine start time, end time, frequency range, etc." );
119
120 LogPrintf( LOG_NORMAL, "%s has length of %u SFT files\n", SFTpatt, catalog->length );
121
122 // If output base name was set by user, use that, otherwise use a default pattern:
123 // spec_<f_min>_<f_max>_<detector>_<GPS-start>_<GPS-end> as the basename
124 if ( XLALUserVarWasSet( &outputBname ) ) {
125 snprintf( outbase, sizeof( outbase ), "%s/%s", outputDir, outputBname );
126 } else {
127 snprintf( outbase, sizeof( outbase ), "%s/spec_%.2f_%.2f_%s_%d_%d", outputDir, f_min, f_max, constraints.detector, startTime.gpsSeconds, endTime.gpsSeconds );
128 }
129
130 // Create filenames from the outbase value
131 snprintf( outfile, sizeof( outfile ), "%s_spectrogram.txt", outbase );
132 snprintf( outfile2, sizeof( outfile2 ), "%s_timestamps.txt", outbase );
133 snprintf( outfile4, sizeof( outfile4 ), "%s_date.txt", outbase );
134
135 // Open the files for writing using the "w" mode, which overwrites files if they exist
136 fp = fopen( outfile, "w" );
137 fopenerr = errno;
138 XLAL_CHECK_MAIN( fp != NULL, XLAL_EIO, "Failed to open '%s' for writing: %s", outfile, strerror( fopenerr ) );
139 fp2 = fopen( outfile2, "w" );
140 fopenerr = errno;
141 XLAL_CHECK_MAIN( fp2 != NULL, XLAL_EIO, "Failed to open '%s' for writing: %s", outfile2, strerror( fopenerr ) );
142 fp4 = fopen( outfile4, "w" );
143 fopenerr = errno;
144 XLAL_CHECK_MAIN( fp4 != NULL, XLAL_EIO, "Failed to open '%s' for writing: %s", outfile4, strerror( fopenerr ) );
145
146 // Write header line to files, if provided
147 if ( XLALUserVarWasSet( &header ) ) {
148 fprintf( fp, "# %s\n", header );
149 fprintf( fp2, "# %s\n", header );
150 fprintf( fp4, "# %s\n", header );
151 }
152
153 // Record timestamps for each SFT or gap
154 // This is not known a priori so we end up resizing this vector as we go
156
157 // Need to save the f0 and deltaF from the first SFT
158 REAL8 f0 = 0, deltaF = 0;
159
160 // Initialize a temporary frequency series for XLALNormalizeSFT
161 REAL8FrequencySeries *rngmed = NULL;
162
163 printf( "Looping over SFTs to compute average spectra and spectrograms\n" );
164 for ( UINT4 j = 0; j < catalog->length; j++ ) {
165 fprintf( stderr, "Extracting SFT %d...\n", j );
166
167 //Extract one SFT at a time from the catalog
168 //we do this by using a catalog timeslice to get just the current SFT
169 XLAL_CHECK_MAIN( ( sft_vect = extract_one_sft( catalog, catalog->data[j].header.epoch, f_min, f_max ) ) != NULL, XLAL_EFUNC );
170 XLAL_CHECK_MAIN( sft_vect->length == 1, XLAL_EINVAL, "Extracted zero SFTs but should have extracted one" );
171
172 //Make sure the SFTs are the same length as what we're expecting from user input
173 XLAL_CHECK_MAIN( fabs( timebaseline * sft_vect->data->deltaF - 1.0 ) <= 10.*LAL_REAL8_EPS, XLAL_EINVAL, "Expected SFTs with length %f but got %f", timebaseline, 1 / sft_vect->data->deltaF );
174
175 // first time through the loop need to allocate timeavg, rngmed
176 // save some scalar values
177 if ( j == 0 ) {
178 // Allocate the vector for the normalized data to be averaged together
179 XLAL_CHECK_MAIN( ( timeavg = XLALCreateREAL4Vector( sft_vect->data->data->length ) ) != NULL, XLAL_EFUNC );
180 memset( timeavg->data, 0, sizeof( REAL4 )*timeavg->length );
181
182 // Compute the number of bins in an average; at minimum this should be 1 bin
183 // It produces the same frequency resolution as specified in the arguments passed to
184 // fscanDriver.py
185 REAL8 spectrogram_blocksize = round( freqres * sft_vect->data->data->length / ( f_max - f_min ) );
186 if ( spectrogram_blocksize < 1.0 ) {
187 NumBinsAvg = 1;
188 } else {
189 NumBinsAvg = ( UINT4 )spectrogram_blocksize;
190 }
191
192 f0 = sft_vect->data->f0;
193 deltaF = sft_vect->data->deltaF;
194
195 // Allocate frequency series for XLALNormalizeSFT; meta data doesn't matter
196 XLAL_CHECK_MAIN( ( rngmed = XLALCreateREAL8FrequencySeries( sft_vect->data->name, &catalog->data[j].header.epoch, f0, deltaF, &sft_vect->data->sampleUnits, sft_vect->data->data->length ) ) != NULL, XLAL_EFUNC );
197
198 fprintf( stderr, "SFTs = %d\tSFT bins = %d\tf0 = %f\n", catalog->length, sft_vect->data->data->length, sft_vect->data->f0 );
199 }
200
201 // GPS time of the current SFT and print to file
202 cur_epoch = sft_vect->data->epoch.gpsSeconds;
203 fprintf( fp2, "%d\t%d\n", timestamps->length, cur_epoch );
204
205 // Get the current UTC time from the GPS seconds of the current SFT and print to file
206 XLAL_CHECK_MAIN( XLALGPSToUTC( &date, cur_epoch ) != NULL, XLAL_EFUNC );
207 fprintf( fp4, "%d\t %i\t %i\t %i\t %i\t %i\t %i\n", timestamps->length, ( date.tm_year + 1900 ), date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec );
208
209 // Here is where the timestamps vector is resized and this epoch is recorded
211 timestamps->data[timestamps->length - 1] = cur_epoch;
212
213 /* Spectrogram (course grained) */
214 // First line will be the frequencies of the spectrogram indicating what is in each column
215 // The first value is 0 simply because column 0 are the GPS times. It keeps the entire
216 // output numeric, in case that matters.
217 if ( j == 0 ) {
218 UINT4 step = 0;
219 for ( UINT4 i = NumBinsAvg - 1; i < sft_vect->data->data->length; i += NumBinsAvg ) {
220 if ( step == 0 ) {
221 fprintf( fp, "0\t" );
222 }
223 fprintf( fp, "%.6f\t", sft_vect->data->f0 + NumBinsAvg * sft_vect->data->deltaF * step );
224 step++;
225 }
226 fprintf( fp, "\n" );
227 }
228
229 // Loop over the number of bins in each SFT, jumping by the average number
230 // Start at the highest bin index and average down. This avoids running off the end of the SFT
231 for ( UINT4 i = NumBinsAvg - 1; i < sft_vect->data->data->length; i += NumBinsAvg ) {
232 // First value in each row is the GPS time
233 if ( i == NumBinsAvg - 1 ) {
234 fprintf( fp, "%i\t", sft_vect->data->epoch.gpsSeconds );
235 }
236 REAL8 avg = 0.0;
237 for ( UINT4 k = 0; k < NumBinsAvg; k++ ) {
238 const REAL8 re = ( REAL8 )crealf( sft_vect->data->data->data[i - k] );
239 const REAL8 im = ( REAL8 )cimagf( sft_vect->data->data->data[i - k] );
240 avg += 2.0 * ( re * re + im * im ) / ( REAL8 )timebaseline;
241 }
242 fprintf( fp, "%e\t", sqrt( avg / ( REAL8 )NumBinsAvg ) ); /* 06/15/2017 gam; then take sqrt here. */
243 }
244 fprintf( fp, "\n" );
245
246 // Fill in gaps where there is no SFT data with zeros
247 if ( j < ( catalog->length - 1 ) ) { /*in all cases except when we are examining the last sft, check that there is no gap to the next sft*/
248 /*test to see if the next SFT immediately follows, if not entries in the matrix until there is one*/
249 while ( cur_epoch + timebaseline < catalog->data[j + 1].header.epoch.gpsSeconds ) {
250 cur_epoch += timebaseline;
251 fprintf( fp2, "%d.\t%d\n", timestamps->length, cur_epoch );
252
253 XLAL_CHECK_MAIN( XLALGPSToUTC( &date, cur_epoch ) != NULL, XLAL_EFUNC );
254 fprintf( fp4, "%d\t %i\t %i\t %i\t %i\t %i\t %i\n", timestamps->length, ( date.tm_year + 1900 ), date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec );
256 timestamps->data[timestamps->length - 1] = cur_epoch;
257
258 for ( UINT4 i = NumBinsAvg - 1; i < sft_vect->data->data->length; i += NumBinsAvg ) {
259 // First value in each row is the GPS time
260 if ( i == NumBinsAvg - 1 ) {
261 fprintf( fp, "%i\t", cur_epoch );
262 }
263 REAL8 avg = 0.0;
264 fprintf( fp, "%e\t", avg );
265 }
266 fprintf( fp, "\n" );
267 }
268
269 }
270
271 /* Normalized, averaged spectra */
272 // Normalize the SFT
273 XLAL_CHECK_MAIN( XLALNormalizeSFT( rngmed, sft_vect->data, blocksRngMed, 0.0 ) == XLAL_SUCCESS, XLAL_EFUNC );
274
275 // Loop over the frequency bins in the SFT
276 for ( UINT4 i = 0; i < sft_vect->data->data->length; i++ ) {
277 const REAL8 re = ( REAL8 )crealf( sft_vect->data->data->data[i] );
278 const REAL8 im = ( REAL8 )cimagf( sft_vect->data->data->data[i] );
279 timeavg->data[i] += re * re + im * im;
280 }
281
282 // Destroys current SFT Vector
283 XLALDestroySFTVector( sft_vect );
284 sft_vect = NULL;
285 }
286 fprintf( stderr, "finished checking for missing sfts, l=%d\n", timestamps->length );
287
288 /*----------------------------------------------------------------------------------------------------------------*/
289
290 // Write the averaged data to files broken up by the user option subband (default: 100 Hz)
291 for ( UINT4 i = 0; i < timeavg->length; i++ ) {
292 REAL8 f_min_subband = f0 + ( ( REAL4 )i ) * deltaF;
293 REAL8 f_max_subband = f_min + subband;
294 if ( f_max_subband > f_max ) {
295 f_max_subband = f_max;
296 }
297
298 if ( fp3 == NULL ) {
299 if ( XLALUserVarWasSet( &outputBname ) ) {
300 snprintf( outbase, sizeof( outbase ), "%s/%s_%.2f_%.2f", outputDir, outputBname, f_min_subband, f_max_subband );
301 } else {
302 snprintf( outbase, sizeof( outbase ), "%s/spec_%.2f_%.2f_%s_%d_%d", outputDir, f_min_subband, f_max_subband, constraints.detector, startTime.gpsSeconds, endTime.gpsSeconds );
303 }
304 snprintf( outfile3, sizeof( outfile3 ), "%s_timeaverage.txt", outbase );
305 fp3 = fopen( outfile3, "w" );
306 fopenerr = errno;
307 XLAL_CHECK_MAIN( fp3 != NULL, XLAL_EIO, "Failed to open '%s' for writing: %s", outfile3, strerror( fopenerr ) );
308 }
309
310 REAL8 f = f0 + ( ( REAL4 )i ) * deltaF;
311 REAL4 PWR = timeavg->data[i] / ( ( REAL4 )catalog->length );
312 fprintf( fp3, "%16.6f %16.3f \n", f, PWR );
313
314 if ( f + deltaF >= f_max_subband ) {
315 fclose( fp3 );
316 fp3 = NULL;
317 }
318 }
319
320 /*------------------------------------------------------------------------------------------------------------------------*/
321 /*End of normal spec_avg code.*/
322
323 XLALDestroySFTCatalog( catalog );
324 XLALDestroyREAL4Vector( timeavg );
327
329
330 /*close all the files, spec_avg.c is done, all info written to the files.*/
331 fclose( fp );
332 fclose( fp2 );
333 fclose( fp4 );
334
335 fprintf( stderr, "end of spec_avg\n" );
336
337 return ( 0 );
338
339}
340/* END main */
#define IFO
int j
int k
const LALVCSInfoList lalPulsarVCSInfoList
NULL-terminated list of VCS and build information for LALPulsar and its dependencies
#define STRING(a)
LIGOTimeGPSVector * timestamps
#define fprintf
SFTVector * extract_one_sft(const SFTCatalog *full_catalog, const LIGOTimeGPS starttime, const REAL8 f_min, const REAL8 f_max)
Definition: fscanutils.c:31
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8FrequencySeries(REAL8FrequencySeries *series)
#define LAL_REAL8_EPS
unsigned char BOOLEAN
double REAL8
#define XLAL_INIT_DECL(var,...)
char CHAR
uint32_t UINT4
int32_t INT4
float REAL4
char char * XLALStringDuplicate(const char *s)
void LogPrintf(LogLevel_t, const char *format,...) _LAL_GCC_PRINTF_FORMAT_(2
LOG_CRITICAL
LOG_NORMAL
int XLALNormalizeSFT(REAL8FrequencySeries *rngmed, SFTtype *sft, UINT4 blockSize, const REAL8 assumeSqrtS)
Normalize an sft based on RngMed estimated PSD, and returns running-median.
void XLALDestroySFTVector(SFTVector *vect)
XLAL interface to destroy an SFTVector.
Definition: SFTtypes.c:300
void XLALDestroySFTCatalog(SFTCatalog *catalog)
Free an 'SFT-catalogue'.
Definition: SFTcatalog.c:329
SFTCatalog * XLALSFTdataFind(const CHAR *file_pattern, const SFTConstraints *constraints)
Find the list of SFTs matching the file_pattern and satisfying the given constraints,...
Definition: SFTcatalog.c:71
const char * lalUserVarHelpBrief
int XLALUserVarReadAllInput(BOOLEAN *should_exit, int argc, char *argv[], const LALVCSInfoList vcs_list)
void XLALDestroyUserVars(void)
const char * lalUserVarHelpDescription
#define XLALRegisterNamedUvar(cvar, name, type, option, category,...)
int XLALUserVarWasSet(const void *cvar)
REAL4Vector * XLALCreateREAL4Vector(UINT4 length)
INT4Vector * XLALResizeINT4Vector(INT4Vector *vector, UINT4 length)
INT4Vector * XLALCreateINT4Vector(UINT4 length)
void XLALDestroyREAL4Vector(REAL4Vector *vector)
void XLALDestroyINT4Vector(INT4Vector *vector)
struct tm * XLALGPSToUTC(struct tm *utc, INT4 gpssec)
#define XLAL_CHECK_MAIN(assertion,...)
#define XLAL_ERROR_MAIN(...)
#define XLAL_TRY(statement, errnum)
XLAL_SUCCESS
XLAL_EUSR0
XLAL_EFUNC
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
float data[BLOCKSIZE]
Definition: hwinject.c:360
int deltaF
int main(int argc, char **argv)
Definition: spec_avg.c:37
CHAR name[LALNameLength]
COMPLEX8Sequence * data
A vector of COMPLEX8FrequencySeries.
COMPLEX8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
COMPLEX8 * data
LIGOTimeGPS * data
array of timestamps
Definition: SFTfileIO.h:193
UINT4 length
number of timestamps
Definition: SFTfileIO.h:192
REAL4 * data
An "SFT-catalogue": a vector of SFTdescriptors, as returned by XLALSFTdataFind()
Definition: SFTfileIO.h:238
SFTDescriptor * data
array of data-entries describing matched SFTs
Definition: SFTfileIO.h:243
UINT4 length
number of SFTs in catalog
Definition: SFTfileIO.h:242
'Constraints' for SFT-matching: which detector, within which time-stretch and which timestamps exactl...
Definition: SFTfileIO.h:212
SFTtype header
SFT-header info.
Definition: SFTfileIO.h:228
double f_min
double f_max