SDDSlib
Loading...
Searching...
No Matches
sddsanalyticsignal.c
Go to the documentation of this file.
1/**
2 * @file sddsanalyticsignal.c
3 * @brief SDDS-format Hilbert transform program.
4 *
5 * This program computes the analytic signal of a real input signal using a Hilbert transform.
6 * The result includes the real part, imaginary part, magnitude, and phase (both wrapped and unwrapped).
7 *
8 * ### Algorithm:
9 * - Perform a complex FFT (Fast Fourier Transform) on the input signal.
10 * - Eliminate negative frequencies by setting the second half of the FFT output to zero.
11 * - Multiply the positive frequencies (except DC and Nyquist) by 2.
12 * - Perform the inverse FFT to obtain the analytic signal.
13 * - Compute the magnitude and phase of the resulting complex signal.
14 *
15 * ### Output Columns:
16 * - `Real<signal>`: Real part of the analytic signal.
17 * - `Imag<signal>`: Imaginary part of the analytic signal.
18 * - `Mag<signal>`: Magnitude of the analytic signal.
19 * - `Arg<signal>`: Wrapped phase of the analytic signal.
20 * - `UnwrappedArg<signal>`: Unwrapped phase of the analytic signal.
21 *
22 * ### Usage:
23 * ```
24 * sddsanalyticsignal [inputfile] [outputfile]
25 * --pipe=<input>,<output>
26 * --columns=<indep-variable>,<depen-quantity>[,...]
27 * --unwrapLimit=<value>
28 * --majorOrder=row|column
29 * ```
30 *
31 * ### Options:
32 * - `--pipe`: Use standard input/output pipes for data.
33 * - `--columns`: Specify the independent and dependent quantities for analysis.
34 * - `--unwrapLimit`: Set the relative magnitude threshold for phase unwrapping.
35 * - `--majorOrder`: Define the output file's major order (`row` or `column`).
36 *
37 * @copyright
38 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
39 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
40 *
41 * @license
42 * This file is distributed under the terms of the Software License Agreement
43 * found in the file LICENSE included with this distribution.
44 *
45 * @author H. Shang, R. Soliday
46 */
47
48#include "mdb.h"
49#include "SDDS.h"
50#include "scan.h"
51#include "fftpackC.h"
52#include "SDDSutils.h"
53#include <ctype.h>
54
55/* Enumeration for option types */
56enum option_type {
57 SET_COLUMN,
58 SET_MAJOR_ORDER,
59 SET_PIPE,
60 SET_UNWRAP_LIMIT,
61 N_OPTIONS
62};
63
64char *option[N_OPTIONS] = {
65 "columns",
66 "majorOrder",
67 "pipe",
68 "unwrapLimit",
69};
70
71static char *USAGE =
72 "Usage: sddsanalyticsignal [<inputfile>] [<outputfile>]\n"
73 " [-pipe=<input>,<output>]\n"
74 " [-unwrapLimit=<value>]\n"
75 " [-columns=<indep-variable>,<depen-quantity>[,...]]\n"
76 " [-majorOrder=row|column]\n\n"
77 "Options:\n"
78 " -pipe Use standard SDDS Toolkit pipe with optional input and output.\n"
79 " -columns Specify the independent variable and dependent quantities to analyze.\n"
80 " <depen-quantity> entries may include wildcards.\n"
81 " -unwrapLimit Set the relative magnitude limit for phase unwrapping.\n"
82 " Phase is only unwrapped when the relative magnitude exceeds this limit.\n"
83 " -majorOrder Define the output file's major order as either 'row' or 'column'.\n\n"
84 "Description:\n"
85 " sddsanalyticsignal computes the complex output of a real signal and generates the following columns:\n"
86 " Real<signal>, Imag<signal>, Mag<signal>, and Arg<signal>.\n"
87 " These represent the Real part, Imaginary part, Magnitude, and Phase of the signal, respectively.\n\n"
88 "Program by Hairong Shang and Louis Emery.\n"
89 "Compiled on " __DATE__ " at " __TIME__ ", SVN revision: " SVN_VERSION "\n";
90
91long process_data(SDDS_DATASET *SDDSout, SDDS_DATASET *SDDSin, int64_t rows, char *depenQuantity, double unwrapLimit);
92
93long create_fft_columns(SDDS_DATASET *SDDSout, SDDS_DATASET *SDDSin, char *origName);
94
95int main(int argc, char **argv) {
96 int iArg;
97 char *indepQuantity, **depenQuantity, **exclude = NULL;
98 long depenQuantities, excludes = 0;
99 char *input, *output;
100 long i, readCode;
101 int64_t rows;
102 int32_t page = 0;
103 unsigned long pipeFlags, majorOrderFlag;
104 SCANNED_ARG *scanned;
105 SDDS_DATASET SDDSin, SDDSout;
106 short columnMajorOrder = -1;
107 double unwrapLimit = 0;
108
110 argc = scanargs(&scanned, argc, argv);
111 if (argc < 3) {
112 fprintf(stderr, "%s\n", USAGE);
113 exit(EXIT_FAILURE);
114 }
115
116 output = input = NULL;
117 pipeFlags = 0;
118 indepQuantity = NULL;
119 depenQuantity = NULL;
120 depenQuantities = 0;
121
122 for (iArg = 1; iArg < argc; iArg++) {
123 if (scanned[iArg].arg_type == OPTION) {
124 /* Process options here */
125 switch (match_string(scanned[iArg].list[0], option, N_OPTIONS, 0)) {
126 case SET_COLUMN:
127 if (indepQuantity)
128 SDDS_Bomb("Only one -columns option may be given");
129 if (scanned[iArg].n_items < 2)
130 SDDS_Bomb("Invalid -columns syntax");
131 indepQuantity = scanned[iArg].list[1];
132 if (scanned[iArg].n_items >= 2) {
133 depenQuantity = tmalloc(sizeof(*depenQuantity) * (depenQuantities = scanned[iArg].n_items - 2));
134 for (i = 0; i < depenQuantities; i++)
135 depenQuantity[i] = scanned[iArg].list[i + 2];
136 }
137 break;
138
139 case SET_PIPE:
140 if (!processPipeOption(scanned[iArg].list + 1, scanned[iArg].n_items - 1, &pipeFlags))
141 SDDS_Bomb("Invalid -pipe syntax");
142 break;
143
144 case SET_MAJOR_ORDER:
145 majorOrderFlag = 0;
146 scanned[iArg].n_items--;
147 if (scanned[iArg].n_items > 0 &&
148 !scanItemList(&majorOrderFlag, scanned[iArg].list + 1, &scanned[iArg].n_items, 0,
149 "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
150 "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL))
151 SDDS_Bomb("Invalid -majorOrder syntax/values");
152 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
153 columnMajorOrder = 1;
154 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
155 columnMajorOrder = 0;
156 break;
157
158 case SET_UNWRAP_LIMIT:
159 if (scanned[iArg].n_items != 2)
160 SDDS_Bomb("Invalid -unwrapLimit syntax/values");
161 if (!get_double(&unwrapLimit, scanned[iArg].list[1]))
162 SDDS_Bomb("Invalid -unwrapLimit syntax/values");
163 break;
164
165 default:
166 fprintf(stderr, "Error: Unknown or ambiguous option: %s\n", scanned[iArg].list[0]);
167 exit(EXIT_FAILURE);
168 break;
169 }
170 } else {
171 if (!input)
172 input = scanned[iArg].list[0];
173 else if (!output)
174 output = scanned[iArg].list[0];
175 else
176 SDDS_Bomb("Too many filenames provided");
177 }
178 }
179
180 processFilenames("sddsanalyticsignal", &input, &output, pipeFlags, 0, NULL);
181
182 if (!indepQuantity)
183 SDDS_Bomb("Supply the independent quantity name with the -columns option");
184
185 if (!SDDS_InitializeInput(&SDDSin, input))
186 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
187
188 if (SDDS_CheckColumn(&SDDSin, indepQuantity, NULL, SDDS_ANY_NUMERIC_TYPE, stderr) != SDDS_CHECK_OKAY)
189 exit(EXIT_FAILURE);
190
191 excludes = appendToStringArray(&exclude, excludes, indepQuantity);
192
193 if (!depenQuantities)
194 depenQuantities = appendToStringArray(&depenQuantity, depenQuantities, "*");
195
196 if ((depenQuantities = expandColumnPairNames(&SDDSin, &depenQuantity, NULL, depenQuantities,
197 exclude, excludes, FIND_NUMERIC_TYPE, 0)) <= 0) {
198 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
199 SDDS_Bomb("No quantities selected to FFT");
200 }
201
202#if 0
203 fprintf(stderr, "%ld dependent quantities:\n", depenQuantities);
204 for (i = 0; i < depenQuantities; i++)
205 fprintf(stderr, " %s\n", depenQuantity[i]);
206#endif
207
208 if (!SDDS_InitializeCopy(&SDDSout, &SDDSin, output, "w"))
209 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
210
211 if (columnMajorOrder != -1)
212 SDDSout.layout.data_mode.column_major = columnMajorOrder;
213 else
214 SDDSout.layout.data_mode.column_major = SDDSin.layout.data_mode.column_major;
215
216 for (i = 0; i < depenQuantities; i++) {
217 if (!create_fft_columns(&SDDSout, &SDDSin, depenQuantity[i])) {
218 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
219 fprintf(stderr, "Error creating output column for %s\n", depenQuantity[i]);
220 exit(EXIT_FAILURE);
221 }
222 }
223
224 if (!SDDS_WriteLayout(&SDDSout))
225 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
226
227 while ((readCode = SDDS_ReadPage(&SDDSin)) > 0) {
228 page++;
229 if ((rows = SDDS_CountRowsOfInterest(&SDDSin)) < 0)
230 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
231 if (rows) {
232 if (!SDDS_StartPage(&SDDSout, rows) || !SDDS_CopyPage(&SDDSout, &SDDSin))
233 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
234 for (i = 0; i < depenQuantities; i++) {
235 if (!process_data(&SDDSout, &SDDSin, rows, depenQuantity[i], unwrapLimit)) {
236 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
237 exit(EXIT_FAILURE);
238 }
239 }
240 } else {
241 if (!SDDS_StartPage(&SDDSout, 0) || !SDDS_CopyParameters(&SDDSout, &SDDSin))
242 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
243 }
244 if (!SDDS_WritePage(&SDDSout))
245 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
246 }
247
248 if (!SDDS_Terminate(&SDDSin)) {
249 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
250 exit(EXIT_FAILURE);
251 }
252
253 if (!SDDS_Terminate(&SDDSout)) {
254 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
255 exit(EXIT_FAILURE);
256 }
257
258 /* free_scanargs(&scanned, argc); */
259
260 return EXIT_SUCCESS;
261}
262
263long process_data(SDDS_DATASET *SDDSout, SDDS_DATASET *SDDSin, int64_t rows, char *depenQuantity, double unwrapLimit) {
264 long index1;
265 int64_t i, n_freq, fftrows, nhalf = 0;
266 double *real, *imag, *real_imag, *data, *magData, *arg, *unwrapped, delta, min, max, phase_correction;
267 char s[256];
268
269 if (!(data = SDDS_GetColumnInDoubles(SDDSin, depenQuantity)))
270 return 0;
271
272 fftrows = rows;
273
274 /* Compute forward FFT */
275 real_imag = tmalloc(sizeof(double) * (2 * fftrows + 2));
276 for (i = 0; i < fftrows; i++) {
277 real_imag[2 * i] = data[i];
278 real_imag[2 * i + 1] = 0;
279 }
280 complexFFT(real_imag, fftrows, 0);
281
282 nhalf = fftrows / 2 + 1;
283
284 /* Remove the second half points by setting them to zero */
285 for (i = nhalf; i < fftrows; i++) {
286 real_imag[2 * i] = 0;
287 real_imag[2 * i + 1] = 0;
288 }
289
290 /* Multiply the first half by 2 except f=0 */
291 for (i = 1; i < nhalf; i++) {
292 if (rows % 2 == 0 && i == rows / 2)
293 continue;
294 real_imag[2 * i] *= 2;
295 real_imag[2 * i + 1] *= 2;
296 }
297
298 /* Compute inverse FFT */
299 complexFFT(real_imag, fftrows, INVERSE_FFT);
300 n_freq = fftrows;
301
302 /* Convert into amplitudes and frequencies, adding phase factor for t[0]!=0 */
303 real = tmalloc(sizeof(double) * n_freq);
304 imag = tmalloc(sizeof(double) * n_freq);
305 magData = tmalloc(sizeof(double) * n_freq);
306 arg = tmalloc(sizeof(*arg) * n_freq);
307 unwrapped = tmalloc(sizeof(*arg) * n_freq);
308 for (i = 0; i < n_freq; i++) {
309 real[i] = real_imag[2 * i];
310 imag[i] = real_imag[2 * i + 1];
311 magData[i] = sqrt(sqr(real[i]) + sqr(imag[i]));
312 if (real[i] || imag[i])
313 arg[i] = 180.0 / PI * atan2(imag[i], real[i]);
314 else
315 arg[i] = 0;
316 }
317
318 /* Find the maximum of magnitude */
319 find_min_max(&min, &max, magData, n_freq);
320
321 /* Compute unwrapped phase using Louis' algorithm */
322 phase_correction = 0;
323 for (i = 0; i < n_freq; i++) {
324 if (i && magData[i] / max > unwrapLimit) {
325 delta = arg[i] - arg[i - 1];
326 if (delta < -180.0)
327 phase_correction += 360.0;
328 else if (delta > 180.0)
329 phase_correction -= 360.0;
330 }
331 unwrapped[i] = arg[i] + phase_correction;
332 }
333
334 sprintf(s, "Real%s", depenQuantity);
335 if ((index1 = SDDS_GetColumnIndex(SDDSout, s)) < 0)
336 return 0;
337
338 if (!SDDS_SetColumn(SDDSout, SDDS_SET_BY_INDEX, real, n_freq, index1) ||
339 !SDDS_SetColumn(SDDSout, SDDS_SET_BY_INDEX, imag, n_freq, index1 + 1) ||
340 !SDDS_SetColumn(SDDSout, SDDS_SET_BY_INDEX, magData, n_freq, index1 + 2) ||
341 !SDDS_SetColumn(SDDSout, SDDS_SET_BY_INDEX, arg, n_freq, index1 + 3) ||
342 !SDDS_SetColumn(SDDSout, SDDS_SET_BY_INDEX, unwrapped, n_freq, index1 + 4))
343 return 0;
344
345 free(data);
346 free(real_imag);
347 free(real);
348 free(imag);
349 free(magData);
350 free(arg);
351 free(unwrapped);
352 return 1;
353}
354
355long create_fft_columns(SDDS_DATASET *SDDSout, SDDS_DATASET *SDDSin, char *origName) {
356 char s[SDDS_MAXLINE];
357 char *origUnits, *origSymbol;
358 char *description, *name, *symbol;
359
360 if (SDDS_GetColumnInformation(SDDSin, "units", &origUnits, SDDS_GET_BY_NAME, origName) != SDDS_STRING ||
361 SDDS_GetColumnInformation(SDDSin, "symbol", &origSymbol, SDDS_GET_BY_NAME, origName) != SDDS_STRING)
362 return 0;
363
364 /* Define Real column */
365 sprintf(s, "Real%s", origName);
366 SDDS_CopyString(&name, s);
367 if (!origSymbol)
368 SDDS_CopyString(&origSymbol, origName);
369 sprintf(s, "Re[%s]", origSymbol);
370 SDDS_CopyString(&symbol, s);
371 sprintf(s, "Real part of %s", origSymbol);
372 SDDS_CopyString(&description, s);
373
374 if (SDDS_DefineColumn(SDDSout, name, symbol, NULL, description, NULL, SDDS_DOUBLE, 0) < 0)
375 return 0;
376 free(name);
377 free(symbol);
378 free(description);
379
380 /* Define Imaginary column */
381 sprintf(s, "Imag%s", origName);
382 SDDS_CopyString(&name, s);
383 if (!origSymbol)
384 SDDS_CopyString(&origSymbol, origName);
385 sprintf(s, "Im[%s]", origSymbol);
386 SDDS_CopyString(&symbol, s);
387 sprintf(s, "Imaginary part of %s", origSymbol);
388 SDDS_CopyString(&description, s);
389
390 if (SDDS_DefineColumn(SDDSout, name, symbol, NULL, description, NULL, SDDS_DOUBLE, 0) < 0)
391 return 0;
392 free(name);
393 free(symbol);
394 free(description);
395
396 /* Define Magnitude column */
397 sprintf(s, "Mag%s", origName);
398 SDDS_CopyString(&name, s);
399
400 if (!origSymbol)
401 SDDS_CopyString(&origSymbol, origName);
402 sprintf(s, "Mag[%s]", origSymbol);
403 SDDS_CopyString(&symbol, s);
404 sprintf(s, "Magnitude of %s", origSymbol);
405 SDDS_CopyString(&description, s);
406 if (SDDS_DefineColumn(SDDSout, name, symbol, NULL, description, NULL, SDDS_DOUBLE, 0) < 0)
407 return 0;
408 free(name);
409 free(symbol);
410 free(description);
411
412 /* Define Phase (Arg) column */
413 sprintf(s, "Arg%s", origName);
414 SDDS_CopyString(&name, s);
415
416 if (!origSymbol)
417 SDDS_CopyString(&origSymbol, origName);
418 sprintf(s, "Arg[%s]", origSymbol);
419 SDDS_CopyString(&symbol, s);
420 sprintf(s, "Phase of %s", origSymbol);
421 SDDS_CopyString(&description, s);
422 if (SDDS_DefineColumn(SDDSout, name, symbol, "degrees", description, NULL, SDDS_DOUBLE, 0) < 0)
423 return 0;
424 free(name);
425 free(symbol);
426 free(description);
427
428 /* Define Unwrapped Phase column */
429 sprintf(s, "UnwrappedArg%s", origName);
430 SDDS_CopyString(&name, s);
431
432 if (!origSymbol)
433 SDDS_CopyString(&origSymbol, origName);
434 sprintf(s, "UnwrappedArg[%s]", origSymbol);
435 SDDS_CopyString(&symbol, s);
436 sprintf(s, "Unwrapped Phase of %s", origSymbol);
437 SDDS_CopyString(&description, s);
438 if (SDDS_DefineColumn(SDDSout, name, symbol, "degrees", description, NULL, SDDS_DOUBLE, 0) < 0)
439 return 0;
440 free(name);
441 free(symbol);
442 free(description);
443
444 return 1;
445}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_CopyParameters(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source)
Definition SDDS_copy.c:286
int32_t SDDS_InitializeCopy(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source, char *filename, char *filemode)
Definition SDDS_copy.c:40
int32_t SDDS_CopyPage(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source)
Definition SDDS_copy.c:578
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int64_t SDDS_CountRowsOfInterest(SDDS_DATASET *SDDS_dataset)
Counts the number of rows marked as "of interest" in the current data table.
double * SDDS_GetColumnInDoubles(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves the data of a specified numerical column as an array of doubles, considering only rows mark...
int32_t SDDS_GetColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Retrieves information about a specified column in the SDDS dataset.
Definition SDDS_info.c:41
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:49
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_ReadPage(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_DefineColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length)
Defines a data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_GetColumnIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named column in the SDDS dataset.
int32_t SDDS_CheckColumn(SDDS_DATASET *SDDS_dataset, char *name, char *units, int32_t type, FILE *fp_message)
Checks if a column exists in the SDDS dataset with the specified name, units, and type.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:432
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:288
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:342
int32_t SDDS_CopyString(char **target, const char *source)
Copies a source string to a target string with memory allocation.
Definition SDDS_utils.c:856
#define SDDS_STRING
Identifier for the string data type.
Definition SDDStypes.h:85
#define SDDS_ANY_NUMERIC_TYPE
Special identifier used by SDDS_Check*() routines to accept any numeric type.
Definition SDDStypes.h:157
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59
int get_double(double *dptr, char *s)
Parses a double value from the given string.
Definition data_scan.c:40
int find_min_max(double *min, double *max, double *list, int64_t n)
Finds the minimum and maximum values in a list of doubles.
Definition findMinMax.c:33
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
long processPipeOption(char **item, long items, unsigned long *flags)
Definition scanargs.c:356
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
Definition scanargs.c:390
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.