-
Notifications
You must be signed in to change notification settings - Fork 2
/
gen_cxform_auto.pl
544 lines (421 loc) · 13.9 KB
/
gen_cxform_auto.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
#!/packages/bin/perl -w
#
# gen_mappings.pl -- make a C file containing mappings to coord xforms
#
# $Id: gen_cxform_auto.pl,v 1.1.1.1 2005/02/25 20:41:41 rboller Exp $
#
# 2003/09/12: Modified by Ryan Boller to remove references to CVS
#
($ME = $0) =~ s,.*/,,;
use strict;
use vars qw($ME $INFILE $RCSID $RCSID_INFILE $MANUAL_VERSION
%MAPS_TO %MAPS_FROM @SYSTEMS);
###############################################################################
# BEGIN user-configurable section
$INFILE = "cxform-manual.c";
# CVS-related code commented out
#my $x = "";
#$RCSID = " \$Id: gen_cxform_auto.pl,v 1.1.1.1 2005/02/25 20:41:41 rboller Exp $x";
#$RCSID = filter_rcsid($RCSID);
# Version of this script
$RCSID = "1.40";
# Version of cxform-manual file
$MANUAL_VERSION = "1.30";
# Security
$ENV{PATH} = "/packages/bin:/usr/bin:/bin";
delete $ENV{CDPATH};
delete $ENV{IFS};
# END user-configurable section
###############################################################################
initialize($INFILE);
#
# Create an output file
#
my $OUT = $INFILE; $OUT =~ s/-manual/-auto/;
my $TMP = $OUT . ".TMP"; unlink $TMP;
my $BAK = $OUT . ".BAK";
open OUT, "> $TMP" or die "$ME: open( >$TMP ): $!\n";
print OUT <<EOP;
static char const Ident[] = \"\@(\#) \$XX: $OUT c:$RCSID_INFILE pl:$RCSID \$\";
/*
** This file is automatically generated. *** DO NOT EDIT BY HAND! ***
**
** Generated by the script \`$ME\', version $RCSID,
** from input \`$INFILE\' version $RCSID_INFILE.
**
** This code is simply a set of wrapper functions for the basic
** coordinate transformations (J2000 to GEI, GEI to GSE, and so on).
** Although there are only a small set of "real" transforms, we want
** to be able to go from one to another without having to think (after
** all, that\'s what computers are for).
**
** This module performs the mappings for going from any coordinate
** system to any other. It also provides the front end "cxform()",
** so the source/destination frames can be passed as strings, instead
** of having to hardcode "gse_to_gsm()" or whatever.
*/
\#include <stdio.h>
\#include <string.h>
\#include "cxform.h"
/*
** First, though, we want a way to convert from a char string ("GSE")
** to a numeric representation. Here we define an enum listing all
** the known coordinate systems, plus UNKNOWN. We then define a
** complicated-looking function that looks at the input string, char
** by char, and returns the numeric representation of the desired
** coordinate system. Note that upper/lower case combos are fine.
*/
EOP
#
# Make an ENUM out of all the known systems, and write a function
# that converts from ASCII input ("gse", "GSE", "gSm") to the enum.
#
printf OUT "enum systems { UNKNOWN, %s };\n", join(", ",@SYSTEMS);
print OUT <<EOP;
enum systems system_number( const char *system )
{
if (system == NULL)
return UNKNOWN;
EOP
gencase(" ", 0, @SYSTEMS); # This function does all the work...
print OUT "}\n";
################
#
# Now define prototypes for all the conversion functions
#
################
print OUT <<EOP;
/*
** The following boring list of prototypes is for the benefit of cxform().
**
** The first cluster, below, lists the functions defined in $INFILE
*/
EOP
# First, a list of prototypes for external (manual) functions
foreach my $from (@SYSTEMS) {
foreach my $to (@SYSTEMS) {
next if $from eq $to;
next unless grep($_ eq $to, @{$MAPS_TO{$from}});
printf OUT "extern int %5s_twixt_%-5s ", lc $from, lc $to;
print OUT "(double t, Vec in, Vec out, Direction d);\n";
}
}
# Next, a list of automatically-defined functions
print OUT <<EOP;
/*
** This second (long) cluster defines the wrappers we generate automatically.
*/
EOP
foreach my $from (@SYSTEMS) {
foreach my $to (@SYSTEMS) {
next if $from eq $to;
printf OUT "static int %5s_to_%-5s (double t, Vec in, Vec out);\n",
lc $from, lc $to;
}
}
################
#
# Now write a mapping function that, when called with a FROM and TO
# coordinate system, and a time, does the appropriate mapping.
#
################
print OUT <<EOP;
static char cxform_err_buf[1024];
char *cxform_err(void)
{
return cxform_err_buf;
}
/*
** Here\'s the main code. Simple, actually. All we do is:
**
** 1) determine the source ("from") and destination ("to") frames,
** and make sure they are valid ones.
**
** 2) call the appropriate <from>_to_<to>() function.
**
** The <from>_to_<to> functions are declared further down below (except
** for the "real" transformation functions, which are in $INFILE).
*/
int cxform(const char *from, const char *to, double t, Vec v_in, Vec v_out)
{
enum systems from_n, to_n;
cxform_err_buf[0] = '\\0';
if (from == NULL) {
strcpy(cxform_err_buf, "No source frame given.");
return 1;
}
if (to == NULL) {
strcpy(cxform_err_buf, "No destination frame given.");
return 1;
}
if ((from_n = system_number(from)) == UNKNOWN) {
sprintf(cxform_err_buf, "Source frame '%s' is unknown;", from);
strcat(cxform_err_buf, " must be one of: @SYSTEMS");
return 1;
}
if ((to_n = system_number(to)) == UNKNOWN) {
sprintf(cxform_err_buf, "Destination frame '%s' is unknown;", to);
strcat(cxform_err_buf, " must be one of: @SYSTEMS");
return 1;
}
/*
** Check to see if the source and destination are the same. If so,
** return the input vector as output.
*/
if (from_n == to_n) {
int i;
for (i=0; i<3; i++)
v_out[i] = v_in[i];
return 0;
}
/* For the "default" cases in the switches below */
strcpy(cxform_err_buf, "Internal error -- this is impossible");
/*
** Find the source, then find the dest, then call the appropriate xform.
*/
switch (from_n) {
EOP
foreach my $from (@SYSTEMS) {
printf OUT " case %s:\n", $from;
printf OUT " switch (to_n) {\n";
foreach my $to (@SYSTEMS) {
next if $from eq $to;
printf OUT " case %s:\n", $to;
printf OUT " return %s_to_%s(t,v_in,v_out);\n", lc $from, lc $to;
}
print OUT " default:\t\t/* Cannot happen */\n";
print OUT " return 1;\n";
printf OUT " }\n";
}
print OUT <<EOP;
default:\t\t/*Cannot happen */
return 1;
}
/* We should never get here */
strcat(cxform_err_buf, "INTERNAL ERROR: switch statements fell through!");
return 2;
}
EOP
##################
#
# Generate the real code
#
##################
print OUT <<EOP;
/*
** Stub routines... all we do is call one of The Six, in various orders
*/
EOP
foreach my $from (@SYSTEMS) {
foreach my $to (@SYSTEMS) {
next if $from eq $to;
write_wrapper_function($from, $to);
}
}
# Close the file, make read-only, and move into place
close OUT or die "$ME: close( $TMP ): $!\n";
chmod 0444, $TMP;
rename $OUT, $BAK;
rename $TMP, $OUT;
system("diff", "-u", $BAK, $OUT);
#
# Now write a DLM definition file
#
my $DLM = $INFILE; $DLM =~ s/-manual//; $DLM =~ s/\.c$/.dlm/;
$TMP = $DLM . ".TMP"; unlink $TMP;
$BAK = $DLM . ".BAK";
open OUT, "> $TMP" or die "$ME: open( >$TMP ): $!\n";
printf OUT <<EOP, version($RCSID, $RCSID_INFILE), date_and_time(), whoami();
MODULE CXFORM
DESCRIPTION Ed Santiago and Ryan Boller\'s Coordinate Transform package
VERSION %s
BUILD_DATE %s
SOURCE %s
FUNCTION CXFORM 0 15
EOP
# Same as above: close the file, chmod, and move.
close OUT or die "$ME: close( $TMP ): $!\n";
chmod 0444, $TMP;
rename $DLM, $BAK;
rename $TMP, $DLM;
system("diff", "-u", $BAK, $DLM);
exit 0;
###################
# date_and_time # returns a string of the form YYYY-MM-DD HH:MM
###################
sub date_and_time {
use Time::localtime;
return sprintf("%04d-%02d-%02d %02d:%02d",
localtime->year+1900,
localtime->mon + 1,
localtime->mday,
localtime->hour,
localtime->min);
}
############
# whoami # returns a string of the form "[email protected]"
############
sub whoami {
use Net::Domain qw(hostfqdn);
my $username = getlogin() || (getpwuid($<))[0] || "Unknown";
my $hostname = hostfqdn();
# Hostname removed due to inaccurate e-mail address
return $username; # sprintf("%s\@%s", $username, $hostname);
}
################################################################
sub initialize {
my $infile = shift;
use Tie::IxHash;
my %seen;
tie %seen, "Tie::IxHash";
# CVS functionality removed - version number is set in header
$RCSID_INFILE = $MANUAL_VERSION; # "<unknown>";
# Reread the infile, looking for the RCS ID and for "_twixt_" declarations
open(IN, $infile) or die "$ME: in initialize(): open( $infile ): $!\n";
while (<IN>) {
# if(/(\$ Id: \s .* \$)/x) {
# $RCSID_INFILE = filter_rcsid($1);
# }
if (/^(\s*(\S+)\s+)?(\w+)_twixt_(\w+)\s*\(\s*const double et/) {
my ($from, $to) = (uc $3, uc $4);
push(@{$MAPS_TO{$from}}, $to);
push(@{$MAPS_FROM{$to}}, $from);
$seen{$from}++;
$seen{$to}++;
}
}
close IN;
@SYSTEMS = keys %seen;
}
##################
# filter_rcsid # from the RCS "Id" string, extract the version number.
##################
sub filter_rcsid($) {
my $id = shift;
local $_;
if ($id =~ /Id:\s+(\S+),v\s+([\d\.]+)\s+/) {
my ($fname, $version) = ($1, $2);
#
# If this version is not up-to-date, add an "x", indicating "experimental"
#
open(CVSTATUS, "cvs status $1 |") or die "$ME: open( cvstatus ): $!\n";
while (<CVSTATUS>) {
if (/Status:\s+(\S.*\S)\s*$/) {
if ($1 ne "Up-to-date") {
$version .= "x";
}
}
}
close CVSTATUS or die "$ME: close( cvstatus ): $!\n";
return $version;
}
return $id;
}
sub gencase {
my ($indent, $pos, @systems) = @_;
printf OUT "%sswitch (system[%d]) {\n", $indent, $pos;
my (%letters) = map { uc substr($_, $pos, 1) => $_ } @systems;
foreach my $letter (sort keys %letters) {
if ($letter ne '') {
my $c = sprintf("%s case '%s':", $indent, lc $letter);
printf OUT "%-47s /* %s */\n", $c,
join(", ", grep(substr($_, $pos, 1) eq $letter, @systems));
printf OUT "%s case '%s':\n",$indent,uc $letter if $letter =~ /[a-z]/i;
gencase($indent . " ",
$pos+1,
grep(substr($_, $pos, 1) eq $letter, @systems));
} else {
printf OUT "%s case '\\0':\n", $indent;
printf OUT "%s return %s;\n", $indent, $letters{$letter};
}
}
printf OUT "%s default:\n", $indent;
printf OUT "%s return UNKNOWN;\n", $indent;
printf OUT "%s}\n", $indent;
}
################################################################
sub write_wrapper_function {
my ($from, $to) = @_;
# See if we have a direct transformation from "from" to "to".
my @seq = sequence($from, $to);
# If there isn't, we must die!
die "no way to go from '$from' to '$to'!\n" if @seq == 0;
printf OUT <<EOP, $from, $to, lc $from, lc $to;
/*
** %s to %s
*/
int %s_to_%s(double t, Vec v_in, Vec v_out)
{
EOP
# Loop starting at 1 (not 0), since we're already in the frame of seq[0]
my $indent = " return";
for (my $i=1; $i < @seq; $i++) {
my $invert = 0;
# Obtain the names of the previous frame ("from") and the destination.
my $f1 = lc $seq[$i-1]; $f1 =~ s/-1$//;
my $f2 = lc $seq[$i]; $invert = ($f2 =~ s/-1$//);
# If the destination has a "-1", what we do is call f2_to_f1 and xpose.
if ($invert) {
($f1, $f2) = ($f2, $f1);
}
my $v1 = ($i == 1 ? "v_in " : "v_out");
my $dir = ($invert ? "BACK" : "FORWARD");
# Emit calls only to gen-yoo-wine manually defined functions
my $l = sprintf("%s %5s_twixt_%-5s(t, %s, v_out, %s)", $indent,
$f1, $f2, $v1, $dir);
# If this is the last step in the sequence, append a semicolon
$l .= ";" if $i == $#seq;
# Append a /* comment */ showing the actual order of the transformation
my (@f1f2) = @seq[$i-1..$i]; map { s/-1$// } @f1f2;
printf OUT "%-55s /* %5s to %-5s */\n", $l, @f1f2;
# After the first call, chain any subsequent calls together via "or".
# This has the result of returning as soon as any one of the chain
# returns non-zero (i.e., error status). If none return nonzero,
# we just return 0.
if ($indent =~ /return/) {
$indent =~ s/./ /g;
$indent =~ s/..$/||/;
}
}
printf OUT "}\n";
return;
}
sub sequence($$..) {
my ($from, $to, @exclude) = @_;
# Try a forward (recursive) search
foreach my $intermediate (@{$MAPS_TO{$from}}) {
return ($from, $to) if ($intermediate eq $to);
next if grep(/^$intermediate(-1)?$/, @exclude);
my @seq = sequence($intermediate, $to, @exclude, $from);
if (@seq > 0) {
return ($from, @seq);
}
}
# ...and do the same, in reverse
foreach my $intermediate (@{$MAPS_FROM{$from}}) {
return ($from, $to . "-1") if ($intermediate eq $to);
next if grep(/^$intermediate(-1)?$/, @exclude);
my @seq = sequence($intermediate, $to, @exclude, $from);
if (@seq > 0) {
return ($from, $seq[0] . "-1", @seq[1..$#seq]);
}
}
# Nope, there's no way to get there from here.
#
# No matter, though. It's possible that we were recursively called
# down the wrong branch, and our caller will find a solution down
# another path.
return ();
}
#############
# version # returns a nicely formatted version string for the .dlm file
#############
sub version {
# Subroutine bypassed due to errors
return $_[0];
open(IN, "VERSION") or die "$ME: open( VERSION ): $!\n";
my $version_official = <IN>;
chop $version_official;
close IN;
return sprintf("%s (development: pl:%s,c:%s)",$version_official,@_);
}