line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Astro::Catalog::IO::JCMT; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Astro::Catalog::IO::JCMT - JCMT catalogue I/O for Astro::Catalog |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$cat = Astro::Catalog::IO::JCMT->_read_catalog( \@lines ); |
10
|
|
|
|
|
|
|
$arrref = Astro::Catalog::IO::JCMT->_write_catalog( $cat, %options ); |
11
|
|
|
|
|
|
|
$filename = Astro::Catalog::IO::JCMT->_default_file(); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 DESCRIPTION |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
This class provides read and write methods for catalogues in the JCMT |
16
|
|
|
|
|
|
|
pointing catalogue format. The methods are not public and should, in general, |
17
|
|
|
|
|
|
|
only be called from the C C and C |
18
|
|
|
|
|
|
|
methods. |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
21
|
|
|
|
|
|
|
|
22
|
1
|
|
|
1
|
|
6262771
|
use 5.006; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
61
|
|
23
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
104
|
|
24
|
1
|
|
|
1
|
|
62
|
use warnings::register; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
478
|
|
25
|
1
|
|
|
1
|
|
8
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
260
|
|
26
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
57
|
|
27
|
|
|
|
|
|
|
|
28
|
1
|
|
|
1
|
|
1113
|
use Astro::Telescope; |
|
1
|
|
|
|
|
42184
|
|
|
1
|
|
|
|
|
42
|
|
29
|
1
|
|
|
1
|
|
720
|
use Astro::Coords; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use Astro::Catalog; |
31
|
|
|
|
|
|
|
use Astro::Catalog::Star; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
use base qw/ Astro::Catalog::IO::ASCII /; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
use vars qw/$VERSION $DEBUG /; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$VERSION = '4.31'; |
38
|
|
|
|
|
|
|
$DEBUG = 0; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Name must be limited to 15 characters on write |
41
|
|
|
|
|
|
|
use constant MAX_SRC_LENGTH => 15; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Default location for a JCMT catalog |
44
|
|
|
|
|
|
|
my $defaultCatalog = "/local/progs/etc/poi.dat"; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Planets appended to the catalogue |
47
|
|
|
|
|
|
|
my @PLANETS = qw/ mercury mars uranus saturn jupiter venus neptune /; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=over 4 |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item B |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Method to take a general target name and clean it up |
54
|
|
|
|
|
|
|
so that it is suitable for writing in a JCMT source catalog. |
55
|
|
|
|
|
|
|
This routine is used by the catalog writing code but can also |
56
|
|
|
|
|
|
|
be used publically in order to make sure that a target name |
57
|
|
|
|
|
|
|
to be written to the catalogue is guaranteed to match that used |
58
|
|
|
|
|
|
|
in another location (e.g. when writing an a document to accompany |
59
|
|
|
|
|
|
|
the catalogue which refers to targets within it). |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
The source name can be truncated. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
$cleaned = Astro::Catalog::IO::JCMT->clean_target_name( $dirty ); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
Will return undef if the argument is not defined. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Punctuation such as "," and ";" are replaced with underscores. |
68
|
|
|
|
|
|
|
".", "()" and "+-" are allowed. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=cut |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub clean_target_name { |
73
|
|
|
|
|
|
|
my $class = shift; |
74
|
|
|
|
|
|
|
my $dirty = shift; |
75
|
|
|
|
|
|
|
return unless defined $dirty; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Remove spaces [compress] |
78
|
|
|
|
|
|
|
$dirty =~ s/\s+//g; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Remove disallowed characters |
81
|
|
|
|
|
|
|
# and replace with dashes |
82
|
|
|
|
|
|
|
$dirty =~ s/[,;:'"`]/-/g; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Truncate it to the allowed length |
85
|
|
|
|
|
|
|
# Name must be limited to MAX_SRC_LENGTH characters |
86
|
|
|
|
|
|
|
$dirty = substr($dirty,0,MAX_SRC_LENGTH); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Return the cleaned name |
89
|
|
|
|
|
|
|
return $dirty; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item B<_default_file> |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Returns the location of the default JCMT pointing catalogue at the |
96
|
|
|
|
|
|
|
JCMT itself. This is purely for convenience of the caller when they |
97
|
|
|
|
|
|
|
are at the JCMT and wish to use the default catalogue without having |
98
|
|
|
|
|
|
|
to know explicitly where it is. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
$filename = Astro::Catalog::IO::JCMT->_default_file(); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Returns empty list/undef if the file is not available. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If the environment variable ASTRO_CATALOG_JCMT is defined (and exists) |
105
|
|
|
|
|
|
|
this will be used as the default. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=cut |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _default_file { |
110
|
|
|
|
|
|
|
my $class = shift; |
111
|
|
|
|
|
|
|
return $ENV{ASTRO_CATALOG_JCMT} |
112
|
|
|
|
|
|
|
if (exists $ENV{ASTRO_CATALOG_JCMT} && -e $ENV{ASTRO_CATALOG_JCMT}); |
113
|
|
|
|
|
|
|
return (-e $defaultCatalog ? $defaultCatalog : () ); |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item B<_read_catalog> |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Parses the catalogue lines and returns a new C |
119
|
|
|
|
|
|
|
object containing the catalog entries. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
$cat = Astro::Catalog::IO::JCMT->_read_catalog( \@lines, %options ); |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Supported options (with defaults) are: |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
telescope => Name of telescope to associate with each coordinate entry |
126
|
|
|
|
|
|
|
(defaults to JCMT). If the telescope option is specified |
127
|
|
|
|
|
|
|
but is undef or empty string, no telescope is used. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
incplanets => Append planets to catalogue entries (default is true) |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=cut |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _read_catalog { |
135
|
|
|
|
|
|
|
my $class = shift; |
136
|
|
|
|
|
|
|
my $lines = shift; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Default options |
139
|
|
|
|
|
|
|
my %defaults = ( telescope => 'JCMT', |
140
|
|
|
|
|
|
|
incplanets => 1); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
my %options = (%defaults, @_); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
croak "Must supply catalogue contents as a reference to an array" |
145
|
|
|
|
|
|
|
unless ref($lines) eq 'ARRAY'; |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Create a new telescope to associate with this |
148
|
|
|
|
|
|
|
my $tel; |
149
|
|
|
|
|
|
|
$tel = new Astro::Telescope( $options{telescope} ) |
150
|
|
|
|
|
|
|
if $options{telescope}; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Go through each line and parse it |
153
|
|
|
|
|
|
|
# and store in the array if we had a successful read |
154
|
|
|
|
|
|
|
my @stars = map { $class->_parse_line( $_, $tel); } @$lines; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Add planets if required |
157
|
|
|
|
|
|
|
if ($options{incplanets}) { |
158
|
|
|
|
|
|
|
# create coordinate objects for the planets |
159
|
|
|
|
|
|
|
my @planets = map { new Astro::Coords(planet => $_) } @PLANETS; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# And associate a telescope |
162
|
|
|
|
|
|
|
if ($tel) { |
163
|
|
|
|
|
|
|
for (@planets) { |
164
|
|
|
|
|
|
|
$_->telescope($tel); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# And create the star objects |
169
|
|
|
|
|
|
|
push(@stars, map { new Astro::Catalog::Star( |
170
|
|
|
|
|
|
|
field => 'JCMT', |
171
|
|
|
|
|
|
|
id => $_->name, |
172
|
|
|
|
|
|
|
coords => $_, |
173
|
|
|
|
|
|
|
comment => 'Added automatically', |
174
|
|
|
|
|
|
|
) } @planets); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Create the catalog object |
179
|
|
|
|
|
|
|
return new Astro::Catalog( Stars => \@stars, |
180
|
|
|
|
|
|
|
Origin => 'JCMT', |
181
|
|
|
|
|
|
|
); |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item B<_write_catalog> |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Write the catalog to an array and return it. Returning a reference to |
188
|
|
|
|
|
|
|
an array provides more flexibility to the caller. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
$ref = Astro::Catalog::IO::JCMT->_write_catalog( $cat ); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Spaces are removed from source names. The contents of the catalog |
193
|
|
|
|
|
|
|
are sanity checked. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub _write_catalog { |
198
|
|
|
|
|
|
|
my $class = shift; |
199
|
|
|
|
|
|
|
my $cat = shift; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Would make more sense to use the array ref here |
202
|
|
|
|
|
|
|
my @sources = $cat->stars; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Counter for unknown targets |
205
|
|
|
|
|
|
|
my $unk = 1; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Hash for storing target information |
208
|
|
|
|
|
|
|
# so that we can search for duplicates |
209
|
|
|
|
|
|
|
my %targets; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# Create hash of all unique target names present |
212
|
|
|
|
|
|
|
# after cleaning. We need this so that we can make sure |
213
|
|
|
|
|
|
|
# a generated name derived from a duplication (with target mismatch) |
214
|
|
|
|
|
|
|
# does not generate a name that already existed explicitly. |
215
|
|
|
|
|
|
|
my %allnames = map { $class->clean_target_name($_->coords->name), undef } |
216
|
|
|
|
|
|
|
@sources; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Loop over each source and extract catalog information |
219
|
|
|
|
|
|
|
# Make sure that we remove unique entries |
220
|
|
|
|
|
|
|
# BUT THAT WE RETAIN THE ORDER OF THE SOURCES IN THE CATALOG |
221
|
|
|
|
|
|
|
# Hence an array for the information |
222
|
|
|
|
|
|
|
my @processed; |
223
|
|
|
|
|
|
|
for my $star (@sources) { |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Extract the coordinate object |
226
|
|
|
|
|
|
|
my $src = $star->coords; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Get the name but do not deal with undef yet |
229
|
|
|
|
|
|
|
# in case the type is not valid |
230
|
|
|
|
|
|
|
my $name = $src->name; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Somewhere to store the extracted information |
233
|
|
|
|
|
|
|
my %srcdata; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Store the name (stripped of spaces) and |
236
|
|
|
|
|
|
|
# treat srcdata{name} as the primary name from here on |
237
|
|
|
|
|
|
|
$srcdata{name} = $class->clean_target_name( $name ); |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Store a comment |
240
|
|
|
|
|
|
|
$srcdata{comment} = $star->comment; |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# prepopulate the default velocity settings |
243
|
|
|
|
|
|
|
$srcdata{rv} = 'n/a'; |
244
|
|
|
|
|
|
|
$srcdata{vdefn} = 'RADIO'; |
245
|
|
|
|
|
|
|
$srcdata{vframe} = 'LSR'; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Get the miscellaneous data. |
248
|
|
|
|
|
|
|
my $misc = $star->misc; |
249
|
|
|
|
|
|
|
if( defined( $misc ) ) { |
250
|
|
|
|
|
|
|
$srcdata{vrange} = ( defined( $misc->{'velocity_range'} ) ? |
251
|
|
|
|
|
|
|
sprintf( "%s", $misc->{'velocity_range'} ) : |
252
|
|
|
|
|
|
|
"n/a" ); |
253
|
|
|
|
|
|
|
$srcdata{flux850} = ( defined( $misc->{'flux850'} ) ? |
254
|
|
|
|
|
|
|
sprintf( "%s", $misc->{'flux850'} ) : |
255
|
|
|
|
|
|
|
"n/a" ); |
256
|
|
|
|
|
|
|
} else { |
257
|
|
|
|
|
|
|
$srcdata{vrange} = "n/a"; |
258
|
|
|
|
|
|
|
$srcdata{flux850} = "n/a"; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Get the type of source |
262
|
|
|
|
|
|
|
my $type = $src->type; |
263
|
|
|
|
|
|
|
if ($type eq 'RADEC') { |
264
|
|
|
|
|
|
|
$srcdata{system} = "RJ"; |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# Need to get the space separated RA/Dec and the sign |
267
|
|
|
|
|
|
|
$srcdata{long} = $src->ra(format => 'array'); |
268
|
|
|
|
|
|
|
$srcdata{lat} = $src->dec(format => 'array'); |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# Get the velocity information |
271
|
|
|
|
|
|
|
my $rv = $src->rv; |
272
|
|
|
|
|
|
|
if ($rv) { |
273
|
|
|
|
|
|
|
$srcdata{rv} = $rv; |
274
|
|
|
|
|
|
|
$srcdata{vdefn} = $src->vdefn; |
275
|
|
|
|
|
|
|
$srcdata{vframe} = $src->vframe; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# JCMT compatibility |
278
|
|
|
|
|
|
|
$srcdata{vframe} = "LSR" if $srcdata{vframe} eq 'LSRK'; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
} elsif ($type eq 'PLANET') { |
283
|
|
|
|
|
|
|
# Planets are not supported in catalog form. Skip them |
284
|
|
|
|
|
|
|
next; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
} elsif ($type eq 'FIXED') { |
287
|
|
|
|
|
|
|
$srcdata{system} = "AZ"; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
$srcdata{long} = $src->az(format => 'array'); |
290
|
|
|
|
|
|
|
$srcdata{lat} = $src->el(format => 'array'); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Need to remove + sign from long/AZ since we are not expecting |
293
|
|
|
|
|
|
|
# it in RA/DEC. This is probably a bug in Astro::Coords |
294
|
|
|
|
|
|
|
shift(@{ $srcdata{long} } ) if $srcdata{long}->[0] eq '+'; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
} else { |
297
|
|
|
|
|
|
|
my $errname = ( defined $srcdata{name} ? $srcdata{name} : ""); |
298
|
|
|
|
|
|
|
warnings::warnif "Coordinate of type $type for target $errname not supported in JCMT catalog files\n"; |
299
|
|
|
|
|
|
|
next; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# Generate a name if not defined |
303
|
|
|
|
|
|
|
if (!defined $srcdata{name}) { |
304
|
|
|
|
|
|
|
$srcdata{name} = "UNKNOWN$unk"; |
305
|
|
|
|
|
|
|
$unk++; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# See if we already have this source and that it is really the |
309
|
|
|
|
|
|
|
# same source Note that we do not see whether this name is the |
310
|
|
|
|
|
|
|
# same as one of the derived names. Eg if CRL618 is in the |
311
|
|
|
|
|
|
|
# pointing catalogue 3 times with identical coords and we add a |
312
|
|
|
|
|
|
|
# new CRL618 with different coords then we trigger 3 warning |
313
|
|
|
|
|
|
|
# messages rather than 1 because we do not check that CRL618_2 is |
314
|
|
|
|
|
|
|
# the same as CRL618_1 |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# Note that velocity specification is included in this comparison |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
if (exists $targets{$srcdata{name}}) { |
319
|
|
|
|
|
|
|
my $previous = $targets{$srcdata{name}}; |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# Create stringified form of previous coordinate with same name |
322
|
|
|
|
|
|
|
# and current coordinate |
323
|
|
|
|
|
|
|
my $prevcoords = join(" ",@{$previous->{long}},@{$previous->{lat}}, |
324
|
|
|
|
|
|
|
$previous->{rv}, $previous->{vdefn}, $previous->{vframe}); |
325
|
|
|
|
|
|
|
my $curcoords = join(" ",@{$srcdata{long}},@{$srcdata{lat}}, |
326
|
|
|
|
|
|
|
$srcdata{rv}, $srcdata{vdefn}, $srcdata{vframe}); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
if ($prevcoords eq $curcoords) { |
329
|
|
|
|
|
|
|
# This is the same target so we can ignore it |
330
|
|
|
|
|
|
|
} else { |
331
|
|
|
|
|
|
|
# Make up a new name. Use the unknown counter for this since |
332
|
|
|
|
|
|
|
# we probably have not used it before. Probably not the best |
333
|
|
|
|
|
|
|
# approach and might have problems in edge cases but good |
334
|
|
|
|
|
|
|
# enough for now |
335
|
|
|
|
|
|
|
my $oldname = $srcdata{name}; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# loop for 100 times |
338
|
|
|
|
|
|
|
my $count; |
339
|
|
|
|
|
|
|
while (1) { |
340
|
|
|
|
|
|
|
# protection loop |
341
|
|
|
|
|
|
|
$count++; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# Try to construct a new name based on a global counter |
344
|
|
|
|
|
|
|
# rather than a counter that starts at 1 for each root |
345
|
|
|
|
|
|
|
my $suffix = "_$unk"; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# increment $unk for next try |
348
|
|
|
|
|
|
|
$unk++; |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# Abort if we have gone round too many times |
351
|
|
|
|
|
|
|
# Making sure that $unk is incremented first |
352
|
|
|
|
|
|
|
if ($count > 100) { |
353
|
|
|
|
|
|
|
$srcdata{name} = substr($oldname,0,int(MAX_SRC_LENGTH/2)) . |
354
|
|
|
|
|
|
|
int(rand(10000)+1000); |
355
|
|
|
|
|
|
|
warn "Uncontrollable looping (or unfeasibly large number of duplicate sources with different coordinates). Panicked and generated random source name of $srcdata{name}.\n"; |
356
|
|
|
|
|
|
|
last; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
# Assume the old name will do fine |
360
|
|
|
|
|
|
|
my $root = $oldname; |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Do not want to truncate the _XX off the end later on |
363
|
|
|
|
|
|
|
if (length($oldname) > MAX_SRC_LENGTH - length($suffix)) { |
364
|
|
|
|
|
|
|
# This may well be confusing but we have no choice. Since |
365
|
|
|
|
|
|
|
# _XX is unique the only time we will get a name clash by |
366
|
|
|
|
|
|
|
# simply chopping the string is if we have a duplicate |
367
|
|
|
|
|
|
|
# that is too long along with a target name that includes |
368
|
|
|
|
|
|
|
# _XX amd matches the truncated source name! |
369
|
|
|
|
|
|
|
$root = substr($oldname, 0, (MAX_SRC_LENGTH-length($suffix)) ); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# Form the new name |
374
|
|
|
|
|
|
|
my $newname = $root . $suffix; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# check to see if this name is in the existing target list |
377
|
|
|
|
|
|
|
next if exists $allnames{$newname}; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# Store it in the targets array and exit loop |
380
|
|
|
|
|
|
|
$srcdata{name} = $newname; |
381
|
|
|
|
|
|
|
last; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# different target |
385
|
|
|
|
|
|
|
warn "Found target with the same name [$oldname] but with different coordinates, renaming it to $srcdata{name}!\n"; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
$targets{$srcdata{name}} = \%srcdata; |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# Store it in the array |
390
|
|
|
|
|
|
|
push(@processed, \%srcdata); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
} else { |
395
|
|
|
|
|
|
|
# Store in hash for easy lookup for duplicates |
396
|
|
|
|
|
|
|
$targets{$srcdata{name}} = \%srcdata; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Store it in the array |
399
|
|
|
|
|
|
|
push(@processed, \%srcdata); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# Output array for new catalog lines |
407
|
|
|
|
|
|
|
my @lines; |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# Write a header |
410
|
|
|
|
|
|
|
push @lines, "*\n"; |
411
|
|
|
|
|
|
|
push @lines, "* Catalog written automatically by class ". __PACKAGE__ ."\n"; |
412
|
|
|
|
|
|
|
push @lines, "* on date " . gmtime . "UT\n"; |
413
|
|
|
|
|
|
|
push @lines, "* Origin of catalogue: ". $cat->origin ."\n"; |
414
|
|
|
|
|
|
|
push @lines, "*\n"; |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# Now need to go through the targets and write them to disk |
417
|
|
|
|
|
|
|
for my $src (@processed) { |
418
|
|
|
|
|
|
|
my $name = $src->{name}; |
419
|
|
|
|
|
|
|
my $long = $src->{long}; |
420
|
|
|
|
|
|
|
my $lat = $src->{lat}; |
421
|
|
|
|
|
|
|
my $system = $src->{system}; |
422
|
|
|
|
|
|
|
my $comment = $src->{comment}; |
423
|
|
|
|
|
|
|
my $rv = $src->{rv}; |
424
|
|
|
|
|
|
|
my $vdefn = $src->{vdefn}; |
425
|
|
|
|
|
|
|
my $vframe = $src->{vframe}; |
426
|
|
|
|
|
|
|
my $vrange = $src->{vrange}; |
427
|
|
|
|
|
|
|
my $flux850 = $src->{flux850}; |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
$comment = '' unless defined $comment; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Velocity can not easily be done with a sprintf since it can be either |
432
|
|
|
|
|
|
|
# a string or a 2 column number |
433
|
|
|
|
|
|
|
if (lc($rv) eq 'n/a') { |
434
|
|
|
|
|
|
|
$rv = ' n/a '; |
435
|
|
|
|
|
|
|
} else { |
436
|
|
|
|
|
|
|
my $sign = ( $rv >= 0 ? '+' : '-' ); |
437
|
|
|
|
|
|
|
my $val = $rv; |
438
|
|
|
|
|
|
|
$val =~ s/^\s*[+-]\s*//; |
439
|
|
|
|
|
|
|
$val =~ s/\s*$//; |
440
|
|
|
|
|
|
|
$rv = $sign . ' '. sprintf('%6.1f',$val); |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# Name must be limited to MAX_SRC_LENGTH characters |
444
|
|
|
|
|
|
|
# [this should be taken care of by clean_target_name but |
445
|
|
|
|
|
|
|
# if we have appended _X.... |
446
|
|
|
|
|
|
|
$name = substr($name,0,MAX_SRC_LENGTH); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
push @lines, |
449
|
|
|
|
|
|
|
sprintf("%-". MAX_SRC_LENGTH. |
450
|
|
|
|
|
|
|
"s %02d %02d %06.3f %1s %02d %02d %04.1f %2s %s %5s %5s %-4s %s %s\n", |
451
|
|
|
|
|
|
|
$name, @$long, @$lat, $system, $rv, $flux850, $vrange, $vframe, $vdefn, $comment); |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
return \@lines; |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=item B<_parse_line> |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Parse a line from a JCMT format catalogue and return a corresponding |
461
|
|
|
|
|
|
|
C object. Returns empty list if the line can not |
462
|
|
|
|
|
|
|
be parsed or refers to a comment line (so that map can be used in the |
463
|
|
|
|
|
|
|
caller). |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
$star = Astro::Catalog::IO::JCMT->_parse_line( $line, $tel ); |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
where C<$line> is the line to be parsed and (optional) C<$tel> |
468
|
|
|
|
|
|
|
is an C object to be associated with the |
469
|
|
|
|
|
|
|
coordinate objects. |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
The line is parsed using a pattern match. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub _parse_line { |
476
|
|
|
|
|
|
|
my $class = shift; |
477
|
|
|
|
|
|
|
my $line = shift; |
478
|
|
|
|
|
|
|
my $tel = shift; |
479
|
|
|
|
|
|
|
chomp $line; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Skip commented and blank lines |
482
|
|
|
|
|
|
|
return if ($line =~ /^\s*[\*\%]/); |
483
|
|
|
|
|
|
|
return if ($line =~ /^\s*$/); |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# Use a pattern match parser |
486
|
|
|
|
|
|
|
my @match = ( $line =~ m/^(.*?) # Target name (non greedy) |
487
|
|
|
|
|
|
|
\s* # optional trailing space |
488
|
|
|
|
|
|
|
(\d{1,2}) # 1 or 2 digits [RA:h] [greedy] |
489
|
|
|
|
|
|
|
\s+ # separator |
490
|
|
|
|
|
|
|
(\d{1,2}) # 1 or 2 digits [RA:m] |
491
|
|
|
|
|
|
|
\s+ # separator |
492
|
|
|
|
|
|
|
(\d{1,2}(?:\.\d*)?) # 1|2 digits opt .fraction [RA:s] |
493
|
|
|
|
|
|
|
# no capture on fraction |
494
|
|
|
|
|
|
|
\s+ |
495
|
|
|
|
|
|
|
([+-]?\s*\d{1,2}) # 1|2 digit [dec:d] inc sign |
496
|
|
|
|
|
|
|
\s+ |
497
|
|
|
|
|
|
|
(\d{1,2}) # 1|2 digit [dec:m] |
498
|
|
|
|
|
|
|
\s+ |
499
|
|
|
|
|
|
|
(\d{1,2}(?:\.\d*)?) # arcsecond (optional fraction) |
500
|
|
|
|
|
|
|
# no capture on fraction |
501
|
|
|
|
|
|
|
\s+ |
502
|
|
|
|
|
|
|
(RJ|RB|GA|AZ) # coordinate type |
503
|
|
|
|
|
|
|
# most everything else is optional |
504
|
|
|
|
|
|
|
# [sign]velocity, flux,vrange,vel_def,frame,comments |
505
|
|
|
|
|
|
|
\s* |
506
|
|
|
|
|
|
|
(n\/a|[+-]\s*\d+(?:\.\d*)?)? # velocity [8] |
507
|
|
|
|
|
|
|
\s* |
508
|
|
|
|
|
|
|
(n\/a|\d+(?:\.\d*)?)? # flux [9] |
509
|
|
|
|
|
|
|
\s* |
510
|
|
|
|
|
|
|
(n\/a|\d+(?:\.\d*)?)? # vel range [10] |
511
|
|
|
|
|
|
|
\s* |
512
|
|
|
|
|
|
|
([\w\/]+)? # vel frame [11] |
513
|
|
|
|
|
|
|
\s* |
514
|
|
|
|
|
|
|
([\w\/]+)? # vel defn [12] |
515
|
|
|
|
|
|
|
\s* |
516
|
|
|
|
|
|
|
(.*)$ # comment [13] |
517
|
|
|
|
|
|
|
/xi); |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# Abort if we do not have matches for the first 8 fields |
520
|
|
|
|
|
|
|
for (0..7) { |
521
|
|
|
|
|
|
|
return unless defined $match[$_]; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
# Read the values |
525
|
|
|
|
|
|
|
my $target = $match[0]; |
526
|
|
|
|
|
|
|
my $ra = join(":",@match[1..3]); |
527
|
|
|
|
|
|
|
my $dec = join(":",@match[4..6]); |
528
|
|
|
|
|
|
|
$dec =~ s/\s//g; # remove space between the sign and number |
529
|
|
|
|
|
|
|
my $epoc = $match[7]; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
print "Creating a new source in _parse_line: $target\n" if $DEBUG; |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# need to translate JCMT epoch to normal epoch |
534
|
|
|
|
|
|
|
my %coords; |
535
|
|
|
|
|
|
|
$epoc = uc($epoc); |
536
|
|
|
|
|
|
|
$coords{name} = $target; |
537
|
|
|
|
|
|
|
if ($epoc eq 'RJ') { |
538
|
|
|
|
|
|
|
$coords{ra} = $ra; |
539
|
|
|
|
|
|
|
$coords{dec} = $dec; |
540
|
|
|
|
|
|
|
$coords{type} = "j2000" |
541
|
|
|
|
|
|
|
} elsif ($epoc eq 'RB') { |
542
|
|
|
|
|
|
|
$coords{ra} = $ra; |
543
|
|
|
|
|
|
|
$coords{dec} = $dec; |
544
|
|
|
|
|
|
|
$coords{type} = "b1950"; |
545
|
|
|
|
|
|
|
} elsif ($epoc eq 'GA') { |
546
|
|
|
|
|
|
|
$coords{long} = $ra; |
547
|
|
|
|
|
|
|
$coords{lat} = $dec; |
548
|
|
|
|
|
|
|
$coords{type} = "galactic"; |
549
|
|
|
|
|
|
|
} elsif ($epoc eq 'AZ') { |
550
|
|
|
|
|
|
|
$coords{az} = $ra; |
551
|
|
|
|
|
|
|
$coords{el} = $dec; |
552
|
|
|
|
|
|
|
$coords{units} = 'sexagesimal'; |
553
|
|
|
|
|
|
|
} else { |
554
|
|
|
|
|
|
|
warnings::warnif "Unknown coordinate type: '$epoc' for target $target. Ignoring line."; |
555
|
|
|
|
|
|
|
return; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# catalog comments are space delimited |
559
|
|
|
|
|
|
|
my $ccol = 13; |
560
|
|
|
|
|
|
|
my $cat_comm = (defined $match[$ccol] ? $match[$ccol] : ''); |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
# Replace multiple spaces in comment with single space |
563
|
|
|
|
|
|
|
$cat_comm =~ s/\s+/ /g; |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# velocity |
566
|
|
|
|
|
|
|
$coords{vdefn} = "RADIO"; |
567
|
|
|
|
|
|
|
$coords{vframe} = "LSR"; |
568
|
|
|
|
|
|
|
if (defined $match[8] && $match[8] !~ /n/) { |
569
|
|
|
|
|
|
|
$match[8] =~ s/\s//g; # remove spaces |
570
|
|
|
|
|
|
|
$coords{rv} = $match[8]; |
571
|
|
|
|
|
|
|
$coords{vdefn} = $match[12]; |
572
|
|
|
|
|
|
|
$coords{vframe} = $match[11]; |
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# create the source object |
576
|
|
|
|
|
|
|
my $source = new Astro::Coords( %coords ); |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
unless (defined $source ) { |
579
|
|
|
|
|
|
|
if ($DEBUG) { |
580
|
|
|
|
|
|
|
print "failed to create source for '$target' and $ra and $dec and $epoc\n"; |
581
|
|
|
|
|
|
|
return; |
582
|
|
|
|
|
|
|
} else { |
583
|
|
|
|
|
|
|
croak "Error parsing line. Unable to create source date for target '$target' at RA '$ra' Dec '$dec' and Epoch '$epoc'\n"; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
$source->telescope( $tel ) if $tel; |
588
|
|
|
|
|
|
|
$source->comment($cat_comm); |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
# Field name should simply be linked to the telescope |
591
|
|
|
|
|
|
|
my $field = (defined $tel ? $tel->name : '' ); |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
my %misc; |
594
|
|
|
|
|
|
|
# Grab the line's velocity range, if it isn't "n/a". |
595
|
|
|
|
|
|
|
if( defined $match[10] && $match[10] !~ /n\/a/ ) { |
596
|
|
|
|
|
|
|
$misc{'velocity_range'} = $match[10]; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# Grab the 850-micron flux, if it isn't "n/a". |
600
|
|
|
|
|
|
|
if( defined $match[9] && $match[9] !~ /n\/a/ ) { |
601
|
|
|
|
|
|
|
$misc{'flux850'} = $match[9]; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
print "Created a new source in _parse_line: $target in field $field\n" if $DEBUG; |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# Now create the star object |
607
|
|
|
|
|
|
|
return new Astro::Catalog::Star( id => $target, |
608
|
|
|
|
|
|
|
coords => $source, |
609
|
|
|
|
|
|
|
field => $field, |
610
|
|
|
|
|
|
|
comment => $cat_comm, |
611
|
|
|
|
|
|
|
misc => \%misc, |
612
|
|
|
|
|
|
|
); |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=back |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head1 NOTES |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
Coordinates are stored as C objects inside |
622
|
|
|
|
|
|
|
C objects. |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head1 GLOBAL VARIABLES |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
The following global variables can be modified to control the state of the |
628
|
|
|
|
|
|
|
module: |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=over 4 |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=item $DEBUG |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
Controls debugging messages. Default state is false. |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=back |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=head1 CONSTANTS |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
The following constants are available for querying: |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=over 4 |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=item MAX_SRC_LENGTH |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
The maximum length of sourcenames writable to a JCMT source catalogue. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=back |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=head1 COPYRIGHT |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
Copyright (C) 1999-2003 Particle Physics and Astronomy Research Council. |
653
|
|
|
|
|
|
|
All Rights Reserved. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=head1 AUTHORS |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Tim Jenness Etjenness@cpan.orgE |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=cut |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
1; |