1 /*
2  * Copyright (c) 2012-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include "log/oskar_log.h"
7 #include "settings/oskar_option_parser.h"
8 #include "utility/oskar_get_error_string.h"
9 #include "utility/oskar_version_string.h"
10 #include "vis/oskar_vis_block.h"
11 #include "vis/oskar_vis_header.h"
12 
13 #include <cfloat>
14 #include <cmath>
15 #include <cstdio>
16 #include <cstdlib>
17 
18 using namespace std;
19 
20 static bool is_compatible(
21         const oskar_VisHeader* vis1, const oskar_VisHeader* vis2);
22 
main(int argc,char ** argv)23 int main(int argc, char** argv)
24 {
25     int status = 0;
26 
27     oskar::OptionParser opt("oskar_vis_add", oskar_version_string());
28     opt.set_description("Application to combine OSKAR visibility files.");
29     opt.add_required("OSKAR visibility files...");
30     opt.add_flag("-o", "Output visibility file name", 1, "out.vis", false, "--output");
31     opt.add_flag("-q", "Disable log messages", false, "--quiet");
32     opt.add_example("oskar_vis_add file1.vis file2.vis");
33     opt.add_example("oskar_vis_add file1.vis file2.vis -o combined.vis");
34     opt.add_example("oskar_vis_add -q file1.vis file2.vis file3.vis");
35     opt.add_example("oskar_vis_add *.vis");
36     if (!opt.check_options(argc, argv)) return EXIT_FAILURE;
37 
38     // Get the options.
39     int num_in_files = 0;
40     const char* const* in_files = opt.get_input_files(2, &num_in_files);
41     const char* out_path = opt.get_string("-o");
42     const bool verbose = opt.is_set("-q") ? false : true;
43     if (num_in_files < 2)
44     {
45         opt.error("Please provide 2 or more visibility files to combine.");
46         return EXIT_FAILURE;
47     }
48 
49     // Print if verbose.
50     if (verbose)
51     {
52         printf("Output visibility file: %s\n", out_path);
53         printf("Combining the %d input files:\n", num_in_files);
54         for (int i = 0; i < num_in_files; ++i)
55         {
56             printf("  [%02d] %s\n", i, in_files[i]);
57         }
58     }
59 
60     // Read all the visibility headers and check consistency.
61     oskar_Binary **files = 0, *out_file = 0;
62     oskar_VisHeader** headers = 0;
63     oskar_VisBlock *block0 = 0, *block1 = 0;
64     files = (oskar_Binary**) calloc(num_in_files, sizeof(oskar_Binary*));
65     headers = (oskar_VisHeader**) calloc(num_in_files, sizeof(oskar_VisHeader*));
66     for (int i = 0; i < num_in_files; ++i)
67     {
68         files[i] = oskar_binary_create(in_files[i], 'r', &status);
69         headers[i] = oskar_vis_header_read(files[i], &status);
70         if (status)
71         {
72             oskar_log_error(0,
73                     "Failed to read visibility data file '%s'", in_files[i]);
74             break;
75         }
76         if (i > 0 && !is_compatible(headers[0], headers[i]))
77         {
78             status = OSKAR_ERR_TYPE_MISMATCH;
79             oskar_log_error(0,
80                     "Input visibility file '%s' does not have "
81                     "the same dimensions", in_files[i]);
82             break;
83         }
84     }
85 
86     // Write the output file using the first header.
87     if (!status)
88     {
89         block0 = oskar_vis_block_create_from_header(
90                 OSKAR_CPU, headers[0], &status);
91         block1 = oskar_vis_block_create_from_header(
92                 OSKAR_CPU, headers[0], &status);
93         oskar_mem_clear_contents(oskar_vis_header_settings(
94                 headers[0]), &status);
95         oskar_mem_clear_contents(oskar_vis_header_telescope_path(
96                 headers[0]), &status);
97         out_file = oskar_vis_header_write(headers[0], out_path, &status);
98         if (status)
99         {
100             oskar_log_error(0,
101                     "Failed to write output visibility data file '%s'",
102                     out_path);
103         }
104     }
105 
106     // Loop over each block in each file.
107     const int num_blocks = oskar_vis_header_num_blocks(headers[0]);
108     for (int b = 0; (b < num_blocks) && !status; ++b)
109     {
110         // Read reference block.
111         oskar_vis_block_read(block0, headers[0], files[0], b, &status);
112 
113         // Read blocks from other files and combine them.
114         for (int i = 1; (i < num_in_files) && !status; ++i)
115         {
116             oskar_vis_block_read(block1, headers[i], files[i], b, &status);
117             if (status)
118             {
119                 oskar_log_error(0, "Failed to read visibility block in '%s'",
120                         in_files[i]);
121                 break;
122             }
123             oskar_Mem* b0 = oskar_vis_block_cross_correlations(block0);
124             const oskar_Mem* b1 =
125                     oskar_vis_block_cross_correlations_const(block1);
126             oskar_mem_add(b0, b0, b1, 0, 0, 0, oskar_mem_length(b0), &status);
127         }
128 
129         // Write combined block.
130         oskar_vis_block_write(block0, out_file, b, &status);
131     }
132 
133     // Free memory and close files.
134     oskar_vis_block_free(block0, &status);
135     oskar_vis_block_free(block1, &status);
136     for (int i = 0; i < num_in_files; ++i)
137     {
138         oskar_vis_header_free(headers[i], &status);
139         oskar_binary_free(files[i]);
140     }
141     oskar_binary_free(out_file);
142     free(files);
143     free(headers);
144 
145     return status ? EXIT_FAILURE : EXIT_SUCCESS;
146 }
147 
is_compatible(const oskar_VisHeader * v1,const oskar_VisHeader * v2)148 static bool is_compatible(const oskar_VisHeader* v1, const oskar_VisHeader* v2)
149 {
150     if (oskar_vis_header_num_channels_total(v1) !=
151             oskar_vis_header_num_channels_total(v2))
152     {
153         return false;
154     }
155     if (oskar_vis_header_max_channels_per_block(v1) !=
156             oskar_vis_header_max_channels_per_block(v2))
157     {
158         return false;
159     }
160     if (oskar_vis_header_num_times_total(v1) !=
161             oskar_vis_header_num_times_total(v2))
162     {
163         return false;
164     }
165     if (oskar_vis_header_max_times_per_block(v1) !=
166             oskar_vis_header_max_times_per_block(v2))
167     {
168         return false;
169     }
170     if (oskar_vis_header_num_stations(v1) !=
171             oskar_vis_header_num_stations(v2))
172     {
173         return false;
174     }
175     if (fabs(oskar_vis_header_freq_start_hz(v1) -
176             oskar_vis_header_freq_start_hz(v2)) > DBL_EPSILON)
177     {
178         return false;
179     }
180     if (fabs(oskar_vis_header_freq_inc_hz(v1) -
181             oskar_vis_header_freq_inc_hz(v2)) > DBL_EPSILON)
182     {
183         return false;
184     }
185     if (oskar_vis_header_amp_type(v1) != oskar_vis_header_amp_type(v2))
186     {
187         return false;
188     }
189     if (oskar_vis_header_pol_type(v1) != oskar_vis_header_pol_type(v2))
190     {
191         return false;
192     }
193 
194     return true;
195 }
196