rms-starcat 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
starcat/ucac4.py ADDED
@@ -0,0 +1,1138 @@
1
+ ################################################################################
2
+ # starcat/ucac4.py
3
+ ################################################################################
4
+
5
+ # Zacharias, N. et al. 2013, The Astronomical Journal, 145, 44
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from pathlib import Path
11
+ import struct
12
+ from typing import Any, Iterator, Optional, cast
13
+
14
+ from filecache import FCPath
15
+ import numpy as np
16
+
17
+ from .starcatalog import (HALFPI,
18
+ MAS_TO_RAD,
19
+ YEAR_TO_SEC,
20
+ Star,
21
+ StarCatalog
22
+ )
23
+
24
+
25
+ class UCAC4Star(Star):
26
+ """A holder for star attributes.
27
+
28
+ This class includes attributes unique to the UCAC4 catalog."""
29
+
30
+ UCAC4_OBJ_TYPE_CLEAN = 0
31
+ UCAC4_OBJ_TYPE_NEAR_OVEREXPOSED = 1
32
+ UCAC4_OBJ_TYPE_STREAK = 2
33
+ UCAC4_OBJ_TYPE_HPM = 3
34
+ UCAC4_OBJ_TYPE_EXT_HPM = 4
35
+ UCAC4_OBJ_TYPE_POOR_PM = 5
36
+ UCAC4_OBJ_TYPE_SUBST_ASTROMETRY = 6
37
+ UCAC4_OBJ_TYPE_SUPPL = 7
38
+ UCAC4_OBJ_TYPE_HPM_NOT_MATCHED = 8
39
+ UCAC4_OBJ_TYPE_HPM_DISCREPANT = 9
40
+
41
+ UCAC4_OBJ_TYPE_STRINGS = ['CLEAN', 'NEAR_OVEREXPOSED', 'STREAK', 'HPM',
42
+ 'EXT_HPM', 'POOR_PM', 'SUBST_ASTROMETRY', 'SUPPL',
43
+ 'HPM_NOT_MATCHED', 'HPM_DISCREPANT']
44
+
45
+ #CATMATCH_TYCHO = 0
46
+ #CATMATCH_AC2000 = 1
47
+ #CATMATCH_AGK2_BONN = 2
48
+ #CATMATCH_AGK2_HAMBURG = 3
49
+ #CATMATCH_ZONE_ASTROGRAPH = 4
50
+ #CATMATCH_BLACK_BIRCH = 5
51
+ #CATMATCH_LICK_ASTROGRAPH = 6
52
+ #CATMATCH_NPM_LICK = 7
53
+ #CATMATCH_SPM_YSJ1 = 8
54
+
55
+ UCAC4_DOUBLE_STAR_FLAG_SINGLE = 0
56
+ UCAC4_DOUBLE_STAR_FLAG_COMP1 = 1
57
+ UCAC4_DOUBLE_STAR_FLAG_COMP2 = 2
58
+ UCAC4_DOUBLE_STAR_FLAG_BLENDED = 3
59
+
60
+ UCAC4_DOUBLE_STAR_FLAG_STRINGS = ['SINGLE', 'COMP1', 'COMP2', 'BLENDED']
61
+
62
+ UCAC4_DOUBLE_STAR_TYPE_NONE = 0
63
+ UCAC4_DOUBLE_STAR_TYPE_1PEAK = 1
64
+ UCAC4_DOUBLE_STAR_TYPE_2PEAK = 2
65
+ UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK = 3
66
+ UCAC4_DOUBLE_STAR_TYPE_1PEAK_FIT = 4
67
+ UCAC4_DOUBLE_STAR_TYPE_2PEAK_FIT = 5
68
+ UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK_FIT = 6
69
+
70
+ UCAC4_DOUBLE_STAR_TYPE_STRINGS = ['NONE', '1PEAK', '2PEAK', 'SECONDARY_PEAK',
71
+ '1PEAK_FIT', '2PEAK_FIT',
72
+ 'SECONDARY_PEAK_FIT']
73
+
74
+ def __init__(self) -> None:
75
+ # Initialize the standard fields
76
+ super().__init__()
77
+
78
+ # Initialize the UCAC4-specific fields
79
+ self.vmag_model: Optional[float] = None
80
+ """Fit model magnitude"""
81
+
82
+ self.obj_type: Optional[int] = None
83
+ """The object type used to identify possible problems with a star or
84
+ the source of data:
85
+ 0 = good, clean star (from MPOS), no known problem;
86
+ 1 = largest flag of any image = near overexposed star (from MPOS);
87
+ 2 = largest flag of any image = possible streak object (from MPOS);
88
+ 3 = high proper motion (HPM) star, match with external PM file (MPOS);
89
+ 4 = actually use external HPM data instead of UCAC4 observ.data
90
+ (accuracy of positions varies between catalogs);
91
+ 5 = poor proper motion solution, report only CCD epoch position;
92
+ 6 = substitute poor astrometric results by FK6/Hip/Tycho-2 data;
93
+ 7 = added supplement star (no CCD data) from FK6/Hip/Tycho-2 data,
94
+ and 2 stars added from high proper motion surveys;
95
+ 8 = high proper motion solution in UCAC4, star not matched with PPMXL;
96
+ 9 = high proper motion solution in UCAC4, discrepant PM to PPMXL
97
+ (see discussion of flags 8,9 in redcution section 2e above)
98
+ """
99
+
100
+ self.double_star_flag: Optional[bool] = None
101
+ """Double star flag overall classification:
102
+ 0 = single star;
103
+ 1 = component #1 of "good" double star;
104
+ 2 = component #2 of "good" double star;
105
+ 3 = blended image
106
+ """
107
+
108
+ self.double_star_type: Optional[int] = None
109
+ """Double star type:
110
+ 0 = no double star, not sufficient #pixels or elongation
111
+ to even call double star fit subroutine;
112
+ 1 = elongated image but no more than 1 peak detected;
113
+ 2 = 2 separate peaks detected -> try double star fit;
114
+ 3 = secondary peak found on each side of primary;
115
+ 4 = case 1 after successful double fit (small separ. blended image);
116
+ 5 = case 2 after successful double fit (most likely real double);
117
+ 6 = case 3 after successful double fit (brighter secondary picked)
118
+ """
119
+
120
+ self.galaxy_match: Optional[float] = None
121
+ """LEDA galaxy match flag:
122
+ This flag is either 0 (no match) or contains the log10 of
123
+ the apparent total diameter for I-band (object size) information
124
+ (unit = 0.1 arcmin) copied from the LEDA catalog (galaxies).
125
+ A size value of less than 1 has been rounded up to 1.
126
+ """
127
+
128
+ self.extended_source: Optional[bool] = None
129
+ """2MASS extended source flag:
130
+ This flag is either 0 (no match) or contains the length of
131
+ the semi-major axis of the fiducial ellipse at the K-band
132
+ (object size) information copied from the 2MASS extended source
133
+ catalog.
134
+ """
135
+
136
+ self.num_img_total: Optional[int] = None
137
+ """Total # of CCD images of this star"""
138
+
139
+ self.num_img_used: Optional[int] = None
140
+ """# of CCD images used for this star.
141
+ A zero for the number of used images indicates that all images
142
+ have some "problem" (such as overexposure). In that case an unweighted
143
+ mean over all available images (na) is taken to derive the mean
144
+ position, while normally a weighted mean was calculated based on
145
+ the "good" images, excluding possible problem images (nu <= na).
146
+ """
147
+
148
+ self.num_cat_pm: Optional[int] = None
149
+ """# catalogs (epochs) used for proper motions"""
150
+
151
+ self.ra_mean_epoch: Optional[float] = None
152
+ """Central epoch for mean RA, minus 1900"""
153
+
154
+ self.dec_mean_epoch: Optional[int] = None
155
+ """Central epoch for mean Dec, minus 1900"""
156
+
157
+ self.cat_match: Optional[list[int]] = None
158
+ """A list of ints indicating which catalogs this star has matched against.
159
+ There are 10 entries: Yale SPM, FK6-Hipparcos-Tycho, AC2000, AGK2 Bonn,
160
+ AKG2 Hamburg, Zone Astrog., Black Birch, Lick Astrog., NPM Lick, and
161
+ SPM YSJ1. See the comments in this file for more details.
162
+ """
163
+
164
+ self.apass_mag_b: Optional[float] = None
165
+ """B magnitude from APASS"""
166
+
167
+ self.apass_mag_v: Optional[float] = None
168
+ """V magnitude from APASS"""
169
+
170
+ self.apass_mag_g: Optional[float] = None
171
+ """G magnitude from APASS"""
172
+
173
+ self.apass_mag_r: Optional[float] = None
174
+ """R magnitude from APASS"""
175
+
176
+ self.apass_mag_i: Optional[float] = None
177
+ """I magnitude from APASS"""
178
+
179
+ self.apass_mag_b_sigma: Optional[float] = None
180
+ """Uncertainty of B magnitude from APASS"""
181
+
182
+ self.apass_mag_v_sigma: Optional[float] = None
183
+ """Uncertainty of V magnitude from APASS"""
184
+
185
+ self.apass_mag_g_sigma: Optional[float] = None
186
+ """Uncertainty of G magnitude from APASS"""
187
+
188
+ self.apass_mag_r_sigma: Optional[float] = None
189
+ """Uncertainty of R magnitude from APASS"""
190
+
191
+ self.apass_mag_i_sigma: Optional[float] = None
192
+ """Uncertainty of I magnitude from APASS"""
193
+
194
+ self.johnson_mag_b: Optional[float] = None
195
+ """The Johnson B magnitude derived from APASS measurements."""
196
+
197
+ self.johnson_mag_v: Optional[float] = None
198
+ """The Johnson V magnitude derived from APASS measurements."""
199
+
200
+ self.id_str: Optional[str] = None
201
+ """A unique name indicating the position in the UCAC4 catalog"""
202
+
203
+ self.id_str_ucac2: Optional[str] = None
204
+ """A unique name indicating the position in the UCAC2 catalog"""
205
+
206
+ def __str__(self) -> str:
207
+
208
+ ret = Star.__str__(self)
209
+
210
+ ret += 'OBJTYPE '
211
+ if self.obj_type is None:
212
+ ret += 'None'
213
+ else:
214
+ ret += UCAC4Star.UCAC4_OBJ_TYPE_STRINGS[self.obj_type]
215
+ ret += ' | '
216
+
217
+ if self.vmag_model is None:
218
+ ret += 'APER VMAG None'
219
+ else:
220
+ ret += f'APER VMAG {self.vmag_model:6.3f}'
221
+ ret += ' | '
222
+
223
+ ret += '\n'
224
+
225
+ ret += 'DBL STAR FLAG='
226
+ if self.double_star_flag is None:
227
+ ret += 'None'
228
+ else:
229
+ ret += UCAC4Star.UCAC4_DOUBLE_STAR_FLAG_STRINGS[self.double_star_flag]
230
+
231
+ ret += ' TYPE='
232
+ if self.double_star_type is None:
233
+ ret += 'None'
234
+ else:
235
+ ret += UCAC4Star.UCAC4_DOUBLE_STAR_TYPE_STRINGS[self.double_star_type]
236
+
237
+ ret += ' | GALAXY '
238
+ if self.galaxy_match is None:
239
+ ret += 'NONE'
240
+ elif self.galaxy_match:
241
+ ret += 'Yes'
242
+ else:
243
+ ret += 'No'
244
+
245
+ ret += ' | EXT SOURCE '
246
+ if self.extended_source is None:
247
+ ret += 'NONE'
248
+ elif self.extended_source:
249
+ ret += 'Yes'
250
+ else:
251
+ ret += 'No'
252
+ ret += '\n'
253
+
254
+ ret += 'APASS '
255
+ if self.apass_mag_b is None:
256
+ ret += 'B None '
257
+ else:
258
+ ret += f'B {self.apass_mag_b:6.3f} +/- {self.apass_mag_b_sigma:6.3f} '
259
+
260
+ if self.apass_mag_v is None:
261
+ ret += 'V None '
262
+ else:
263
+ ret += f'V {self.apass_mag_v:6.3f} +/- {self.apass_mag_v_sigma:6.3f} '
264
+
265
+ if self.apass_mag_g is None:
266
+ ret += 'G None '
267
+ else:
268
+ ret += f'G {self.apass_mag_g:6.3f} +/- {self.apass_mag_g_sigma:6.3f} '
269
+
270
+ if self.apass_mag_r is None:
271
+ ret += 'R None '
272
+ else:
273
+ ret += f'R {self.apass_mag_r:6.3f} +/- {self.apass_mag_r_sigma:6.3f} '
274
+
275
+ if self.apass_mag_i is None:
276
+ ret += 'I None'
277
+ else:
278
+ ret += f'I {self.apass_mag_i:6.3f} +/- {self.apass_mag_i_sigma:6.3f}'
279
+
280
+ ret += '\n'
281
+
282
+ if self.johnson_mag_b is None or self.johnson_mag_v is None:
283
+ ret += 'JOHNSON B None V None'
284
+ else:
285
+ ret += f'JOHNSON B {self.johnson_mag_b:6.3f} V {self.johnson_mag_v:6.3f}'
286
+ ret += '\n'
287
+
288
+ return ret
289
+
290
+ # TODO
291
+ # self.cat_match = None
292
+ # self.num_img_total = None
293
+ # self.num_img_used = None
294
+ # self.num_cat_pm = None
295
+ # self.ra_mean_epoch = None
296
+ # self.dec_mean_epoch = None
297
+ # self.id_str = None
298
+ # self.id_str_ucac2 = None
299
+
300
+
301
+ #col byte item fmt unit explanation notes
302
+ #---------------------------------------------------------------------------
303
+ # 1 1- 4 ra I*4 mas right ascension at epoch J2000.0 (ICRS) (1)
304
+ # 2 5- 8 spd I*4 mas south pole distance epoch J2000.0 (ICRS) (1)
305
+ # 3 9-10 magm I*2 millimag UCAC fit model magnitude (2)
306
+ # 4 11-12 maga I*2 millimag UCAC aperture magnitude (2)
307
+ # 5 13 sigmag I*1 1/100 mag error of UCAC magnitude (3)
308
+ # 6 14 objt I*1 object type (4)
309
+ # 7 15 cdf I*1 combined double star flag (5)
310
+ # 15 bytes
311
+ # 8 16 sigra I*1 mas s.e. at central epoch in RA (*cos Dec) (6)
312
+ # 9 17 sigdc I*1 mas s.e. at central epoch in Dec (6)
313
+ #10 18 na1 I*1 total # of CCD images of this star
314
+ #11 19 nu1 I*1 # of CCD images used for this star (7)
315
+ #12 20 cu1 I*1 # catalogs (epochs) used for proper motions
316
+ # 5 bytes
317
+ #13 21-22 cepra I*2 0.01 yr central epoch for mean RA, minus 1900
318
+ #14 23-24 cepdc I*2 0.01 yr central epoch for mean Dec,minus 1900
319
+ #15 25-26 pmrac I*2 0.1 mas/yr proper motion in RA*cos(Dec) (8)
320
+ #16 27-28 pmdc I*2 0.1 mas/yr proper motion in Dec
321
+ #17 29 sigpmr I*1 0.1 mas/yr s.e. of pmRA * cos Dec (9)
322
+ #18 30 sigpmd I*1 0.1 mas/yr s.e. of pmDec (9)
323
+ # 10 bytes
324
+ #19 31-34 pts_key I*4 2MASS unique star identifier (10)
325
+ #20 35-36 j_m I*2 millimag 2MASS J magnitude
326
+ #21 37-38 h_m I*2 millimag 2MASS H magnitude
327
+ #22 39-40 k_m I*2 millimag 2MASS K_s magnitude
328
+ #23 41 icqflg I*1 2MASS cc_flg*10 + ph_qual flag for J (11)
329
+ #24 42 (2) I*1 2MASS cc_flg*10 + ph_qual flag for H (11)
330
+ #25 43 (3) I*1 2MASS cc_flg*10 + ph_qual flag for K_s (11)
331
+ #26 44 e2mpho I*1 1/100 mag error 2MASS J magnitude (12)
332
+ #27 45 (2) I*1 1/100 mag error 2MASS H magnitude (12)
333
+ #28 46 (3) I*1 1/100 mag error 2MASS K_s magnitude (12)
334
+ # 16 bytes
335
+ #29 47-48 apasm I*2 millimag B magnitude from APASS (13)
336
+ #30 49-50 (2) I*2 millimag V magnitude from APASS (13)
337
+ #31 51-52 (3) I*2 millimag g magnitude from APASS (13)
338
+ #32 53-54 (4) I*2 millimag r magnitude from APASS (13)
339
+ #33 55-56 (5) I*2 millimag i magnitude from APASS (13)
340
+ #34 57 apase I*1 1/100 mag error of B magnitude from APASS (14)
341
+ #35 58 (2) I*1 1/100 mag error of V magnitude from APASS (14)
342
+ #36 59 (3) I*1 1/100 mag error of g magnitude from APASS (14)
343
+ #37 60 (4) I*1 1/100 mag error of r magnitude from APASS (14)
344
+ #38 61 (5) I*1 1/100 mag error of i magnitude from APASS (14)
345
+ #39 62 gcflg I*1 Yale SPM g-flag*10 c-flag (15)
346
+ # 16 bytes
347
+ #40 63-66 icf(1) I*4 FK6-Hipparcos-Tycho source flag (16)
348
+ #41 icf(2) .. AC2000 catalog match flag (17)
349
+ #42 icf(3) .. AGK2 Bonn catalog match flag (17)
350
+ #43 icf(4) .. AKG2 Hamburg catalog match flag (17)
351
+ #44 icf(5) .. Zone Astrog. catalog match flag (17)
352
+ #45 icf(6) .. Black Birch catalog match flag (17)
353
+ #46 icf(7) .. Lick Astrog. catalog match flag (17)
354
+ #47 icf(8) .. NPM Lick catalog match flag (17)
355
+ #48 icf(9) .. SPM YSJ1 catalog match flag (17)
356
+ # 4 bytes
357
+ #49 67 leda I*1 LEDA galaxy match flag (18)
358
+ #50 68 x2m I*1 2MASS extend.source flag (19)
359
+ #51 69-72 rnm I*4 unique star identification number (20)
360
+ #52 73-74 zn2 I*2 zone number of UCAC2 (0 = no match) (21)
361
+ #53 75-78 rn2 I*4 running record number along UCAC2 zone (21)
362
+
363
+ UCAC4_FMT = '=iihhbbbbbbbbhhhhbbihhhbbbbbbhhhhhbbbbbbibbihi'
364
+ UCAC4_RECORD_SIZE = 78
365
+ assert struct.calcsize(UCAC4_FMT) == UCAC4_RECORD_SIZE
366
+
367
+ UCAC4_FMT_RA = '=i'
368
+ UCAC4_RECORD_SIZE_RA = 4
369
+ assert struct.calcsize(UCAC4_FMT_RA) == UCAC4_RECORD_SIZE_RA
370
+
371
+
372
+ class UCAC4StarCatalog(StarCatalog):
373
+ """A UCAC4 star catalog.
374
+
375
+ This class adds the following options to `find_stars`::
376
+
377
+ require_clean (bool, default True): Return only "clean" stars. Skip stars
378
+ that are streaks or have high proper motion that is not matched
379
+ or discrepant.
380
+ allow_double (bool, default True): If True, include double stars.
381
+ allow_galaxy (bool, default False): If True, include galaxies.
382
+ require_pm (bool, default True): If True, only return stars with measured proper
383
+ motion.
384
+ return_everything (bool, default False): If True, override the above options to
385
+ return all stars and galaxies.
386
+ optimize_ra (bool, default True): Optimize searching of RA through zone files.
387
+ Only useful for internal development.
388
+ """
389
+
390
+ def __init__(self,
391
+ dir: Optional[str | Path | FCPath] = None) -> None:
392
+ """Create a UCAC4StarCatalog.
393
+
394
+ Parameters:
395
+ dir: The path to the star catalog directory (may be a URL). Within
396
+ this directory should be the directory ``u4b``.
397
+ """
398
+
399
+ super().__init__()
400
+
401
+ if dir is None:
402
+ self._dirname = FCPath(os.environ['UCAC4_PATH'])
403
+ else:
404
+ self._dirname = FCPath(dir)
405
+ self.debug_level = 0
406
+
407
+ def _find_stars(self,
408
+ ra_min: float,
409
+ ra_max: float,
410
+ dec_min: float,
411
+ dec_max: float,
412
+ vmag_min: Optional[float] = None,
413
+ vmag_max: Optional[float] = None,
414
+ full_result: bool = True,
415
+ **kwargs: Any) -> Iterator[UCAC4Star]:
416
+
417
+ # We do this here instead of as specific arguments because it works better
418
+ # with mypy
419
+ require_clean: bool = kwargs.pop('require_clean', True)
420
+ allow_double: bool = kwargs.pop('allow_double', True)
421
+ allow_galaxy: bool = kwargs.pop('allow_galaxy', False)
422
+ require_pm: bool = kwargs.pop('require_pm', True)
423
+ return_everything: bool = kwargs.pop('return_everything', False)
424
+ optimize_ra: bool = kwargs.pop('optimize_ra', True)
425
+
426
+ start_znum = int(max(np.floor((np.degrees(dec_min)+90)*5)+1, 1))
427
+ end_znum = int(min(np.floor((np.degrees(dec_max)+90-1e-15)*5)+1, 900))
428
+
429
+ for znum in range(start_znum, end_znum+1):
430
+ fn = self._zone_filename(znum)
431
+ with fn.open(mode='rb') as fp:
432
+ for star in self._find_stars_one_file(znum, fp,
433
+ ra_min, ra_max,
434
+ dec_min, dec_max,
435
+ vmag_min=vmag_min,
436
+ vmag_max=vmag_max,
437
+ require_clean=require_clean,
438
+ allow_double=allow_double,
439
+ allow_galaxy=allow_galaxy,
440
+ require_pm=require_pm,
441
+ return_everything=return_everything,
442
+ optimize_ra=optimize_ra,
443
+ full_result=full_result):
444
+ yield star
445
+
446
+ def _find_stars_one_file(self,
447
+ znum: int,
448
+ fp: Any,
449
+ ra_min: float,
450
+ ra_max: float,
451
+ dec_min: float,
452
+ dec_max: float,
453
+ vmag_min: Optional[float] = None,
454
+ vmag_max: Optional[float] = None,
455
+ require_clean: bool = True,
456
+ allow_double: bool = False,
457
+ allow_galaxy: bool = False,
458
+ require_pm: bool = True,
459
+ return_everything: bool = False,
460
+ optimize_ra: bool = True,
461
+ full_result: bool = True,
462
+ **kwargs: Any) -> Iterator[UCAC4Star]:
463
+ """Yield the results for a single zone."""
464
+
465
+ if return_everything:
466
+ require_clean = False
467
+ allow_double = True
468
+ allow_galaxy = True
469
+ require_pm = False
470
+
471
+ if optimize_ra:
472
+ rec_num = self._find_starting_ra(fp, ra_min) # 0-based
473
+ else:
474
+ rec_num = 0
475
+
476
+ while True:
477
+ record = fp.read(UCAC4_RECORD_SIZE)
478
+ if len(record) != UCAC4_RECORD_SIZE:
479
+ break
480
+ rec_num += 1 # This makes rec_num 1-based
481
+ parsed = struct.unpack(UCAC4_FMT, record)
482
+ star = UCAC4Star()
483
+
484
+ ###########
485
+ # RA, DEC #
486
+ ###########
487
+
488
+ # 1 1- 4 ra I*4 mas right ascension at epoch J2000.0 (ICRS) (1)
489
+ # 2 5- 8 spd I*4 mas south pole distance epoch J2000.0 (ICRS) (1)
490
+ # Note (1): Positions are on the International Celestial Reference
491
+ # System (ICRS) as represented by the Hipparcos / Tycho-2 catalogs.
492
+ # The epoch for the positions of all stars is J2000.0; the weighted
493
+ # mean catalog position was updated using the provided proper
494
+ # motions. The observational UCAC position is but one of several
495
+ # going into these values and is not given in the UCAC4; thus the
496
+ # original UCAC observation cannot be recovered from these data.
497
+ # The declination is given in south pole distance (spd) and can be
498
+ # converted back to a true declination by subtracting 324000000 mas.
499
+ star.ra = cast(float, parsed[0] * MAS_TO_RAD)
500
+ star.dec = cast(float, parsed[1] * MAS_TO_RAD - HALFPI)
501
+ if star.ra >= ra_max:
502
+ # RA is in ascending order in the file
503
+ if self.debug_level > 1:
504
+ print('ID', parsed[42], 'SKIPPED RA AND REST OF FILE', star.ra)
505
+ break
506
+ if (star.ra < ra_min or
507
+ star.dec < dec_min or star.dec >= dec_max):
508
+ if self.debug_level > 1:
509
+ print('ID', parsed[42], 'SKIPPED RA/DEC', star.ra, star.dec)
510
+ continue
511
+
512
+ #############
513
+ # MAGNITUDE #
514
+ #############
515
+
516
+ # 3 9-10 magm I*2 millimag UCAC fit model magnitude (2)
517
+ # 4 11-12 maga I*2 millimag UCAC aperture magnitude (2)
518
+ # 5 13 sigmag I*1 1/100 mag error of UCAC magnitude (3)
519
+ # Note (2): Unknown, or unrealistic photometric results are set to
520
+ # magnitude = 20 (20000 mmag entry in catalog). Systematic errors
521
+ # are expected to be below 0.1 mag for magm,maga photometric results
522
+ # obtained from the UCAC CCD pixel data. The aperture photometry
523
+ # is considered more robust, particularly for "odd" cases, while
524
+ # the model fit magnitude is expected to be more accurate for
525
+ # "well behaved" stars.
526
+ #
527
+ # Note (3): A value of 99 for error in magnitude means "no data".
528
+ # For many stars a photometric error based on the scatter from
529
+ # individual observations of that star on different CCD frames
530
+ # could be obtained. A model error was also attempted to be
531
+ # assigned, based on the S/N ratio. The error quoted here is
532
+ # the larger of the 2. If that error exceeds 0.9 mag the error
533
+ # was set to 0.9 mag (= value 90 in catalog data, unit = 10 mmag).
534
+ if parsed[2] == 20000:
535
+ star.vmag_model = None
536
+ else:
537
+ star.vmag_model = parsed[2] / 1000.
538
+ if parsed[3] == 20000:
539
+ star.vmag = None
540
+ else:
541
+ star.vmag = parsed[3] / 1000.
542
+ if parsed[4] == 99:
543
+ star.vmag_sigma = None
544
+ else:
545
+ star.vmag_sigma = parsed[4] / 100.
546
+ if vmag_min is not None:
547
+ if star.vmag is None or star.vmag < vmag_min:
548
+ if self.debug_level > 1:
549
+ print('ID', parsed[42], 'SKIPPED MODEL MAG', star.vmag_model)
550
+ continue
551
+ if vmag_max is not None:
552
+ if star.vmag is None or star.vmag > vmag_max:
553
+ if self.debug_level > 1:
554
+ print('ID', parsed[42], 'SKIPPED MODEL MAG', star.vmag_model)
555
+ continue
556
+
557
+ ###############
558
+ # OBJECT TYPE #
559
+ ###############
560
+
561
+ # 6 14 objt I*1 object type (4)
562
+ # Note (4): The object type flag is used to identify possible problems
563
+ # with a star or the source of data. Of the individual image flags
564
+ # the one with the largest value (worst problem case) is propagated
565
+ # into this object type flag, unless it is superseded by an overriding
566
+ # flag at the combined image stage.
567
+ # The object type flag has the following meaning:
568
+ #
569
+ # 0 = good, clean star (from MPOS), no known problem
570
+ # 1 = largest flag of any image = near overexposed star (from MPOS)
571
+ # 2 = largest flag of any image = possible streak object (from MPOS)
572
+ # 3 = high proper motion (HPM) star, match with external PM file (MPOS)
573
+ # 4 = actually use external HPM data instead of UCAC4 observ.data
574
+ # (accuracy of positions varies between catalogs)
575
+ # 5 = poor proper motion solution, report only CCD epoch position
576
+ # 6 = substitute poor astrometric results by FK6/Hip/Tycho-2 data
577
+ # 7 = added supplement star (no CCD data) from FK6/Hip/Tycho-2 data,
578
+ # and 2 stars added from high proper motion surveys
579
+ # 8 = high proper motion solution in UCAC4, star not matched with PPMXL
580
+ # 9 = high proper motion solution in UCAC4, discrepant PM to PPMXL
581
+ # (see discussion of flags 8,9 in reduction section 2e above)
582
+ star.obj_type = parsed[5]
583
+ if (require_clean and
584
+ (star.obj_type in (UCAC4Star.UCAC4_OBJ_TYPE_STREAK,
585
+ UCAC4Star.UCAC4_OBJ_TYPE_HPM_NOT_MATCHED,
586
+ UCAC4Star.UCAC4_OBJ_TYPE_HPM_DISCREPANT))):
587
+ # Use with extreme caution
588
+ if self.debug_level:
589
+ print('ID', parsed[42], 'SKIPPED NOT CLEAN', star.obj_type)
590
+ continue
591
+
592
+ #############################
593
+ # COMBINED DOUBLE STAR FLAG #
594
+ #############################
595
+
596
+ # 7 15 cdf I*1 combined double star flag (5)
597
+ # Note (5): The cdf flag is a combined double star flag used to indicate
598
+ # the type/quality of double star fit. It is a combination of 2 flags,
599
+ # cdf = 10 * dsf + dst with the following meaning:
600
+ #
601
+ # dsf = double star flag = overall classification
602
+ # 0 = single star
603
+ # 1 = component #1 of "good" double star
604
+ # 2 = component #2 of "good" double star
605
+ # 3 = blended image
606
+ #
607
+ # dst = double star type, from pixel data image profile fits,
608
+ # largest value of all images used for this star
609
+ # 0 = no double star, not sufficient #pixels or elongation
610
+ # to even call double star fit subroutine
611
+ # 1 = elongated image but no more than 1 peak detected
612
+ # 2 = 2 separate peaks detected -> try double star fit
613
+ # 3 = secondary peak found on each side of primary
614
+ # 4 = case 1 after successful double fit (small separ. blended image)
615
+ # 5 = case 2 after successful double fit (most likely real double)
616
+ # 6 = case 3 after successful double fit (brighter secondary picked)
617
+ #
618
+ # A word of caution: often a dsf= 1 or 2 image is paired with a dsf= 3.
619
+ # If for a star any of the several images reveals a "blended image",
620
+ # that higher dsf=3 flag is carried into the output file. This can
621
+ # happen for a regular double star with unique components 1 and 2.
622
+ # A flag dsf=3 means this could be component 1 or 2 but at least on
623
+ # one CCD frame a blended image was detected. This blend could be
624
+ # with the other component, or a spurious image or artifact.
625
+ # The double star flags need to be interpreted with caution; anything
626
+ # but a zero means "likely some double star component or blended image".
627
+ cdf = parsed[6]
628
+ if not allow_double and cdf != 0:
629
+ if self.debug_level:
630
+ print('ID', parsed[42], 'SKIPPED DOUBLE', cdf)
631
+ continue
632
+ star.double_star_flag = cdf // 10
633
+ star.double_star_type = cdf % 10
634
+
635
+ #################################
636
+ # GALAXIES AND EXTENDED SOURCES #
637
+ #################################
638
+
639
+ #(41)49 67 leda I*1 LEDA galaxy match flag (18)
640
+ #(42)50 68 x2m I*1 2MASS extend.source flag (19)
641
+ # Note (18): This flag is either 0 (no match) or contains the log10 of
642
+ # the apparent total diameter for I-band (object size) information
643
+ # (unit = 0.1 arcmin) copied from the LEDA catalog (galaxies).
644
+ # A size value of less than 1 has been rounded up to 1.
645
+ #
646
+ # Note (19): This flag is either 0 (no match) or contains the length of
647
+ # the semi-major axis of the fiducial ellipse at the K-band
648
+ # (object size) information copied from the 2MASS extended source
649
+ # catalog.
650
+ star.galaxy_match = parsed[40]
651
+ star.extended_source = parsed[41] # XXX What units is this in?
652
+ if not allow_galaxy and (star.galaxy_match or star.extended_source):
653
+ if self.debug_level:
654
+ print('ID', parsed[42], 'SKIPPED GALAXY/EXTENDED',
655
+ star.galaxy_match, star.extended_source)
656
+ continue
657
+ if star.galaxy_match:
658
+ star.galaxy_match = 10.**star.galaxy_match / 10. / 60. # Degrees
659
+
660
+ #################
661
+ # PROPER MOTION #
662
+ #################
663
+
664
+ #15 25-26 pmrac I*2 0.1 mas/yr proper motion in RA*cos(Dec) (8)
665
+ #16 27-28 pmdc I*2 0.1 mas/yr proper motion in Dec
666
+ #17 29 sigpmr I*1 0.1 mas/yr s.e. of pmRA * cos Dec (9)
667
+ #18 30 sigpmd I*1 0.1 mas/yr s.e. of pmDec (9)
668
+ # Note (8): A value of 32767 for either proper motion component means
669
+ # the real PM of that star is larger and found in the extra table
670
+ # file u4hpm.dat (32 stars, ASCII). The cross reference is established
671
+ # by the unique, modified MPOS number (column 51 of main data file),
672
+ # which is also given on the HPM supplement stars file.
673
+ # For stars without valid proper motion the proper motion data are set
674
+ # to 0. However, valid proper motions can also be 0. The "no data"
675
+ # case is indicated by the sigma proper motion columns (see below).
676
+ #
677
+ # Note (9): Values in the binary data files are represented as signed,
678
+ # 1-byte integer (range -128 to 127). Add 128 to bring those values
679
+ # to the range of 0 to 255, which is the error in proper motion in
680
+ # unit of 0.1 mas/yr, with the following exception.
681
+ # Data entries above 250 indicate larger errors as follows:
682
+ # 251 --> 275 = 27.5 mas/yr
683
+ # 252 --> 325 = 32.5 mas/yr
684
+ # 253 --> 375 = 37.5 mas/yr
685
+ # 254 --> 450 = 45.0 mas/yr
686
+ # 255 --> "no data" = set to 500 for output tables
687
+ #
688
+ # For astrometric data copied from the FK6, Hipparcos and Tycho-2
689
+ # catalogs a mean error in positions was adopted depending on input
690
+ # catalog and the brightness of the star rather than giving the
691
+ # individual star's error quoted in those catalogs.
692
+ star.pm_rac = parsed[14] * 0.1 * MAS_TO_RAD * YEAR_TO_SEC
693
+ star.pm_ra = star.pm_rac / np.cos(star.dec)
694
+ star.pm_dec = parsed[15] * 0.1 * MAS_TO_RAD * YEAR_TO_SEC
695
+
696
+ if parsed[14] == 32767 or parsed[15] == 32767:
697
+ # PM is too large and needs to be looked up in another
698
+ # table, which we don't support yet. XXX
699
+ star.pm_rac = None
700
+ star.pm_ra = None
701
+ star.pm_dec = None
702
+
703
+ prse = parsed[16]+128
704
+ pdse = parsed[17]+128
705
+ star.pm_rac_sigma = prse * 0.1 * MAS_TO_RAD * YEAR_TO_SEC
706
+ star.pm_dec_sigma = pdse * 0.1 * MAS_TO_RAD * YEAR_TO_SEC
707
+ if prse == 251:
708
+ star.pm_rac_sigma = 27.5 * MAS_TO_RAD * YEAR_TO_SEC
709
+ elif prse == 252:
710
+ star.pm_rac_sigma = 32.5 * MAS_TO_RAD * YEAR_TO_SEC
711
+ elif prse == 253:
712
+ star.pm_rac_sigma = 37.5 * MAS_TO_RAD * YEAR_TO_SEC
713
+ elif prse == 254:
714
+ star.pm_rac_sigma = 45.0 * MAS_TO_RAD * YEAR_TO_SEC
715
+ elif prse == 255:
716
+ star.pm_rac_sigma = None
717
+ if star.pm_rac == 0:
718
+ star.pm_rac = None
719
+ if star.pm_rac_sigma is None:
720
+ star.pm_ra_sigma = None
721
+ else:
722
+ star.pm_ra_sigma = (star.pm_rac_sigma /
723
+ np.cos(star.dec))
724
+
725
+ if pdse == 251:
726
+ star.pm_dec_sigma = 27.5 * MAS_TO_RAD
727
+ elif pdse == 252:
728
+ star.pm_dec_sigma = 32.5 * MAS_TO_RAD
729
+ elif pdse == 253:
730
+ star.pm_dec_sigma = 37.5 * MAS_TO_RAD
731
+ elif pdse == 254:
732
+ star.pm_dec_sigma = 45.0 * MAS_TO_RAD
733
+ elif pdse == 255:
734
+ star.pm_dec_sigma = None
735
+ if star.pm_dec == 0:
736
+ star.pm_dec = None
737
+
738
+ if require_pm and (star.pm_ra is None or star.pm_dec is None):
739
+ if self.debug_level:
740
+ print('ID', parsed[42], 'SKIPPED NO PM', parsed[14:18])
741
+ continue
742
+
743
+ #################################
744
+ #################################
745
+ ### END OF SELECTION CRITERIA ###
746
+ #################################
747
+ #################################
748
+
749
+ if not full_result:
750
+ if self.debug_level:
751
+ print('ID', parsed[42], 'OK!')
752
+ yield star
753
+ continue
754
+
755
+ ###########################
756
+ # RA/DEC SYSTEMATIC ERROR #
757
+ ###########################
758
+
759
+ # 8 16 sigra I*1 mas s.e. at central epoch in RA (*cos Dec) (6)
760
+ # 9 17 sigdc I*1 mas s.e. at central epoch in Dec (6)
761
+ # Note (6): The range of values here is 1 to 255 which is represented
762
+ # as a signed 1-byte integer (range -127 to 127); thus add 128 to the
763
+ # integer number found in the data file. There is no 0 mas value;
764
+ # data less than 1 mas have been set to 1 mas. Original data larger
765
+ # than 255 mas have been set to 255.
766
+ # If the astrometric data for a star was substituted from an external
767
+ # catalog like Hipparcos, Tycho or high proper motion data, a mean
768
+ # error in position and proper motion depending on the catalog and
769
+ # magnitude of the star was adopted.
770
+ star.rac_sigma = (parsed[7]+128.) * MAS_TO_RAD # RA * COS(DEC)
771
+ star.ra_sigma = star.rac_sigma / np.cos(star.dec)
772
+ star.dec_sigma = (parsed[8]+128.) * MAS_TO_RAD
773
+
774
+ ##############
775
+ # IMAGE INFO #
776
+ ##############
777
+
778
+ #10 18 na1 I*1 total # of CCD images of this star
779
+ #11 19 nu1 I*1 # of CCD images used for this star (7)
780
+ #12 20 cu1 I*1 # catalogs (epochs) used for proper motions
781
+ # Note (7): A zero for the number of used images indicates that all images
782
+ # have some "problem" (such as overexposure). In that case an unweighted
783
+ # mean over all available images (na) is taken to derive the mean
784
+ # position, while normally a weighted mean was calculated based on
785
+ # the "good" images, excluding possible problem images (nu <= na).
786
+ star.num_img_total = parsed[9]
787
+ star.num_img_used = parsed[10]
788
+ star.num_cat_pm = parsed[11]
789
+
790
+ #########
791
+ # EPOCH #
792
+ #########
793
+
794
+ #13 21-22 cepra I*2 0.01 yr central epoch for mean RA, minus 1900
795
+ #14 23-24 cepdc I*2 0.01 yr central epoch for mean Dec,minus 1900
796
+ star.ra_mean_epoch = parsed[12] * 0.01 + 1900
797
+ star.dec_mean_epoch = parsed[13] * 0.01 + 1900
798
+
799
+ ##############
800
+ # 2MASS DATA #
801
+ ##############
802
+
803
+ #19 31-34 pts_key I*4 2MASS unique star identifier (10)
804
+ #20 35-36 j_m I*2 millimag 2MASS J magnitude
805
+ #21 37-38 h_m I*2 millimag 2MASS H magnitude
806
+ #22 39-40 k_m I*2 millimag 2MASS K_s magnitude
807
+ #23 41 icqflg I*1 2MASS cc_flg*10 + ph_qual flag for J (11)
808
+ #24 42 (2) I*1 2MASS cc_flg*10 + ph_qual flag for H (11)
809
+ #25 43 (3) I*1 2MASS cc_flg*10 + ph_qual flag for K_s (11)
810
+ #26 44 e2mpho I*1 1/100 mag error 2MASS J magnitude (12)
811
+ #27 45 (2) I*1 1/100 mag error 2MASS H magnitude (12)
812
+ #28 46 (3) I*1 1/100 mag error 2MASS K_s magnitude (12)
813
+ # Note (10): The 2MASS items copied into UCAC4 are described at
814
+ # pegasus.astro.umass.edu/ipac_wget/releases/allsky/doc/sec2_2a.html
815
+ #
816
+ # Note (11): For each 2MASS bandpass a combined flag was created
817
+ # (cc_flg*10 + ph_qual) consisting of the contamination flag (0 to 5)
818
+ # and the photometric quality flag (0 to 8).
819
+ #
820
+ # 0 = cc_flg 2MASS 0, no artifacts or contamination
821
+ # 1 = cc_flg 2MASS p, source may be contaminated by a latent image
822
+ # 2 = cc_flg 2MASS c, photometric confusion
823
+ # 3 = cc_flg 2MASS d, diffraction spike confusion
824
+ # 4 = cc_flg 2MASS s, electronic stripe
825
+ # 5 = cc_flg 2MASS b, bandmerge confusion
826
+ #
827
+ # 0 = no ph_qual flag
828
+ # 1 = ph_qual 2MASS X, no valid brightness estimate
829
+ # 2 = ph_qual 2MASS U, upper limit on magnitude
830
+ # 3 = ph_qual 2MASS F, no reliable estimate of the photometric error
831
+ # 4 = ph_qual 2MASS E, goodness-of-fit quality of profile-fit poor
832
+ # 5 = ph_qual 2MASS A, valid measurement, [jhk]snr>10 AND [jhk]cmsig<0.10857
833
+ # 6 = ph_qual 2MASS B, valid measurement, [jhk]snr> 7 AND [jhk]cmsig<0.15510
834
+ # 7 = ph_qual 2MASS C, valid measurement, [jhk]snr> 5 AND [jhk]cmsig<0.21714
835
+ # 8 = ph_qual 2MASS D, valid measurement, no [jhk]snr OR [jhk]cmsig req.
836
+ #
837
+ # For example icqflg = 05 is decoded to be cc_flg=0, and ph_qual=5, meaning
838
+ # no artifacts or contamination from cc_flg and 2MASS qual flag = "A" .
839
+ #
840
+ # Note (12): The photometric errors from 2MASS were rounded by 1 digit
841
+ # here to fit into fewer bytes (1/100 mag instead of millimag).
842
+ # These data were taken from the j_msigcom, h_msigcom, and k_msigcom columns
843
+ # of the 2MASS point source catalog. See note (10).
844
+
845
+ ##############
846
+ # APASS DATA #
847
+ ##############
848
+
849
+ #29 47-48 apasm I*2 millimag B magnitude from APASS (13)
850
+ #30 49-50 (2) I*2 millimag V magnitude from APASS (13)
851
+ #31 51-52 (3) I*2 millimag g magnitude from APASS (13)
852
+ #32 53-54 (4) I*2 millimag r magnitude from APASS (13)
853
+ #33 55-56 (5) I*2 millimag i magnitude from APASS (13)
854
+ #34 57 apase I*1 1/100 mag error of B magnitude from APASS (14)
855
+ #35 58 (2) I*1 1/100 mag error of V magnitude from APASS (14)
856
+ #36 59 (3) I*1 1/100 mag error of g magnitude from APASS (14)
857
+ #37 60 (4) I*1 1/100 mag error of r magnitude from APASS (14)
858
+ #38 61 (5) I*1 1/100 mag error of i magnitude from APASS (14)
859
+ # Note (13): Data are from the AAVSO Photometric all-sky survey (APASS)
860
+ # DR6 plus single observation stars kindly provided by A.Henden.
861
+ # A magnitude entry of 20000 indicates "no data". For bright stars
862
+ # the apasm(1) = B mag and apasm(2) = V mag columns contain the
863
+ # Hipparcos/Tycho Bt and Vt mags respectively, whenever there is no
864
+ # APASS B or V available and valid Bt or Vt mags were found.
865
+ # For the bright supplement stars the same was done. All thses cases
866
+ # are identified by apasm(1) < 20000 and apase(1) = 0 for B mags,
867
+ # and similarly for apasm(2) < 20000 and apase(2) = 0 for V mags.
868
+ # For over 10,000 stars no Vt mag was available and the V mag from Tycho
869
+ # was used instead.
870
+ #
871
+ # Note (14): Positive errors are from the official release data error
872
+ # estimates (at least 2 observations per star). Formal, S/N estimated
873
+ # errors for single observations are multiplied by -1 for this column.
874
+ # The valid range for each APASS magnitude error is +-90 = +-0.90 mag.
875
+ # For "no data" (i.e. magnitude = 20000 = 20.0 mag) the error is set to 99.
876
+ # ============================================================================
877
+ # APASS is described in:
878
+ #
879
+ # Henden, A. A., et al. 2009, The AAVSO Photometric All-Sky Survey,
880
+ # AAS 214, 0702.
881
+ #
882
+ # Henden, A. A., et al. 2010, New Results from the AAVSO Photometric
883
+ # All Sky Survey, AAS 215, 47011.
884
+ #
885
+ # Henden, A. A., et al. 2011,The AAVSO Photometric All-Sky Survey,
886
+ # in prep.
887
+ #
888
+ # Smith, T. C., et al. 2010, AAVSO Photometric All-Sky Survey
889
+ # Implementation at the Dark Ridge Observatory, SAS.
890
+ #
891
+ # APASS provides magnitudes in five pass bands: Johnson B and V, and Sloan
892
+ # g', r', and i'.
893
+ #
894
+ # The Johnson system is described in:
895
+ #
896
+ # Johnson, H.L. and Morgan, W.W. 1953, The Astrophysical Journal, 117,
897
+ # 313
898
+ #
899
+ # Johnson filter transmission data can be found at:
900
+ #
901
+ # http://obswww.unige.ch/gcpd/filters/fil01.html
902
+ #
903
+ # ftp://obsftp.unige.ch/pub/mermio/filters/ph01.Bj
904
+ # ftp://obsftp.unige.ch/pub/mermio/filters/ph01.Vj
905
+ #
906
+ # The SDSS photometric system is described in:
907
+ #
908
+ # Fukugita, M. et al. 1996, The Astronomical Journal, 111, 1748
909
+ #
910
+ # SDSS filter transmission data can be found at:
911
+ #
912
+ # http://www.sdss.org/dr1/instruments/imager/index.html#filters
913
+ #
914
+ # http://www.sdss.org/dr1/instruments/imager/filters/g.dat
915
+ # http://www.sdss.org/dr1/instruments/imager/filters/r.dat
916
+ # http://www.sdss.org/dr1/instruments/imager/filters/i.dat
917
+ #
918
+ # The Tycho-2 photometric system is described in:
919
+ #
920
+ # The Hipparcos and Tycho Catalogues, ESA SP-1200, Vol. 1, June 1997.
921
+ #
922
+ # Bt and Vt filter transmission data is shown in Table 1.3.1 of SP-1200.
923
+ #
924
+ # ESA SP-1200 p. 57 provides the following conversion from Tycho V_T and B_T
925
+ # to Johnson V, B:
926
+ #
927
+ # V_J = V_T - 0.090 * (B_T - V_T)
928
+ # (B_J - V_J) = 0.850 * (B_T - V_T)
929
+ # or
930
+ # B_J = V_J + 0.850 * (B_T - V_T)
931
+ #
932
+ # These transformations fail for M-type stars and apply only to unreddened
933
+ # stars.
934
+ if parsed[28] != 20000:
935
+ star.apass_mag_b = parsed[28] * .001
936
+ if parsed[29] != 20000:
937
+ star.apass_mag_v = parsed[29] * .001
938
+ if parsed[30] != 20000:
939
+ star.apass_mag_g = parsed[30] * .001
940
+ if parsed[31] != 20000:
941
+ star.apass_mag_r = parsed[31] * .001
942
+ if parsed[32] != 20000:
943
+ star.apass_mag_i = parsed[32] * .001
944
+ star.apass_mag_b_sigma = parsed[33] * .01
945
+ star.apass_mag_v_sigma = parsed[34] * .01
946
+ star.apass_mag_g_sigma = parsed[35] * .01
947
+ star.apass_mag_r_sigma = parsed[36] * .01
948
+ star.apass_mag_i_sigma = parsed[37] * .01
949
+
950
+ star.johnson_mag_b = star.apass_mag_b
951
+ star.johnson_mag_v = star.apass_mag_v
952
+
953
+ if (star.apass_mag_b is not None and
954
+ parsed[33] == 0 and
955
+ star.apass_mag_v is not None and
956
+ parsed[34] == 0):
957
+ star.johnson_mag_v = (star.apass_mag_v -
958
+ 0.09 * (star.apass_mag_b -
959
+ star.apass_mag_v))
960
+ star.johnson_mag_b = (star.johnson_mag_v +
961
+ 0.85 * (star.apass_mag_b -
962
+ star.apass_mag_v))
963
+
964
+ #29 47-48 apasm I*2 millimag B magnitude from APASS (13)
965
+ #30 49-50 (2) I*2 millimag V magnitude from APASS (13)
966
+ #31 51-52 (3) I*2 millimag g magnitude from APASS (13)
967
+ #32 53-54 (4) I*2 millimag r magnitude from APASS (13)
968
+ #33 55-56 (5) I*2 millimag i magnitude from APASS (13)
969
+ #34 57 apase I*1 1/100 mag error of B magnitude from APASS (14)
970
+ #35 58 (2) I*1 1/100 mag error of V magnitude from APASS (14)
971
+ #36 59 (3) I*1 1/100 mag error of g magnitude from APASS (14)
972
+ #37 60 (4) I*1 1/100 mag error of r magnitude from APASS (14)
973
+ #38 61 (5) I*1 1/100 mag error of i magnitude from APASS (14)
974
+
975
+ #######################
976
+ # CATALOG MATCH FLAGS #
977
+ #######################
978
+
979
+ #39 62 gcflg I*1 Yale SPM g-flag*10 c-flag (15)
980
+ #40 63-66 icf(1) I*4 FK6-Hipparcos-Tycho source flag (16)
981
+ #41 icf(2) .. AC2000 catalog match flag (17)
982
+ #42 icf(3) .. AGK2 Bonn catalog match flag (17)
983
+ #43 icf(4) .. AKG2 Hamburg catalog match flag (17)
984
+ #44 icf(5) .. Zone Astrog. catalog match flag (17)
985
+ #45 icf(6) .. Black Birch catalog match flag (17)
986
+ #46 icf(7) .. Lick Astrog. catalog match flag (17)
987
+ #47 icf(8) .. NPM Lick catalog match flag (17)
988
+ #48 icf(9) .. SPM YSJ1 catalog match flag (17)
989
+ #
990
+ # Note (15): The g-flag from the Yale San Juan first epoch Southern
991
+ # Proper Motion data (YSJ1, SPM) has the following meaning:
992
+ #
993
+ # 0 = no info
994
+ # 1 = matched with 2MASS extended source list
995
+ # 2 = LEDA galaxy
996
+ # 3 = known QSO
997
+ #
998
+ # The c-flag from the Yale San Juan first epoch Southern
999
+ # Proper Motion data (YSJ1, SPM) indicates which input catalog
1000
+ # has been used to identify stars for pipeline processing:
1001
+ #
1002
+ # 1 = Hipparcos
1003
+ # 2 = Tycho2
1004
+ # 3 = UCAC2
1005
+ # 4 = 2MASS psc
1006
+ # 5 = 2MASS xsc (extended sources, largely (but not all!) galaxies)
1007
+ # 6 = LEDA (confirmed galaxies, Paturel et al. 2005)
1008
+ # 7 = QSO (Veron-Cetty & Veron 2006)
1009
+ # Note (16, 17) binary data: a single 4-byte integer is used to store
1010
+ # the 10 flags of "icf". That 4-byte integer has the value:
1011
+ # icf = icf(1)*10^8 + icf(2)*10^7 + ... + icf(8)*10 + icf(9)
1012
+ #
1013
+ # Note (16): The FK6-Hipparcos-Tycho-source-flag has the following meaning:
1014
+ # (= icf(1))
1015
+ # 0 = not a Hip. or Tycho star
1016
+ # 1 = Hipparcos 1997 version main catalog (not in UCAC4 data files)
1017
+ # 2 = Hipparcos double star annex
1018
+ # 3 = Tycho-2
1019
+ # 4 = Tycho annex 1
1020
+ # 5 = Tycho annex 2
1021
+ # 6 = FK6 position and proper motion (instead of Hipparcos data)
1022
+ # 7 = Hippparcos 2007 solution position and proper motion
1023
+ # 8 = FK6 only PM substit. (not in UCAC4 data)
1024
+ # 9 = Hipparcos 2007, only proper motion substituted
1025
+ #
1026
+ # Note (17): The catflg match flag is provided for major catalogs used
1027
+ # in the computation of the proper motions. Each match is analyzed
1028
+ # for multiple matches of entries of the 1st catalog to 2nd catalog
1029
+ # entries, and the other way around. Matches are also classified
1030
+ # by separation and difference in magnitude to arrive at a confidence
1031
+ # level group. The flag has the following meaning:
1032
+ #
1033
+ # 0 = star not matched with this catalog
1034
+ # 1 = unique-unique match, not involving a double star
1035
+ # 2 = ... same, but involving a flagged double star
1036
+ # 3 = multiple match but unique in high confidence level group, no double
1037
+ # 4 = ... same, but involving a flagged double star
1038
+ # 5 = closest match, not involving a double, likely o.k.
1039
+ # 6 = ... same, but involving a flagged double star
1040
+ # 7 = maybe o.k. smallest sep. match in both directions, no double
1041
+ # 8 = ... same, but involving a flagged double star
1042
+ star.cat_match = [int(x) for x in ('%010d' % parsed[39])]
1043
+
1044
+ ###################
1045
+ # UCAC4 UNIQUE ID #
1046
+ ###################
1047
+
1048
+ #(43)51 69-72 rnm I*4 unique star identification number (20)
1049
+ # Note (20): This unique star identification number is between 200001
1050
+ # and 321640 for Hipparcos stars, and between 1 and 9430 for non-
1051
+ # Hipparcos stars supplemented to the UCAC4 catalog (no CCD observ.).
1052
+ # For all other stars this unique star identification number is the
1053
+ # internal mean-position-file (MPOS) number + 1 million.
1054
+ # For both the Hipparcos and the supplement stars there is an entry
1055
+ # on the u4supl.dat file providing more information, including the
1056
+ # original Hipparcos star number. Note, there are several thousand
1057
+ # cases where different UCAC4 stars link to the same Hipparcos star
1058
+ # number due to resolved binary stars with each component being a
1059
+ # separate star entry in UCAC4.
1060
+ star.unique_number = parsed[42]
1061
+
1062
+ ###############################
1063
+ # UCAC2 IDENTIFICATION NUMBER #
1064
+ ###############################
1065
+
1066
+ #(44)52 73-74 zn2 I*2 zone number of UCAC2 (0 = no match) (21)
1067
+ #(45)53 75-78 rn2 I*4 running record number along UCAC2 zone (21)
1068
+
1069
+ if parsed[43] == 0:
1070
+ star.id_str_ucac2 = None
1071
+ else:
1072
+ star.id_str_ucac2 = f'UCAC2-{parsed[43]:03d}-{parsed[44]:06d}'
1073
+
1074
+ ###############################
1075
+ # UCAC4 IDENTIFICATION NUMBER #
1076
+ ###############################
1077
+
1078
+ star.id_str = f'UCAC4-{znum:03d}-{rec_num:06d}'
1079
+
1080
+ ##################################################
1081
+ # COMPUTE SPECTRAL CLASS AND SURFACE TEMPERATURE #
1082
+ ##################################################
1083
+
1084
+ if (star.johnson_mag_b is not None and
1085
+ star.johnson_mag_v is not None):
1086
+ star.spectral_class = Star.sclass_from_bv(star.johnson_mag_b,
1087
+ star.johnson_mag_v)
1088
+
1089
+ if star.spectral_class is not None:
1090
+ star.temperature = Star.temperature_from_sclass(star.
1091
+ spectral_class)
1092
+
1093
+ if self.debug_level:
1094
+ print('ID', parsed[42], 'OK!')
1095
+ print(star)
1096
+ yield star
1097
+
1098
+ #############################################################################
1099
+
1100
+ def _zone_filename(self, znum: int) -> FCPath:
1101
+ """Convert a UCAC4 zone number to an absolute pathspec."""
1102
+
1103
+ return self._dirname / 'u4b' / f'z{znum:03d}'
1104
+
1105
+ def _find_starting_ra(self,
1106
+ fp: Any,
1107
+ ra_min: float) -> int:
1108
+ """Efficiently find the first record >= RA_MIN."""
1109
+
1110
+ if ra_min <= 0.:
1111
+ # No point in searching!
1112
+ return 0
1113
+
1114
+ fp.seek(0, os.SEEK_END)
1115
+ file_size = fp.tell()
1116
+ num_rec = file_size // UCAC4_RECORD_SIZE
1117
+ assert num_rec * UCAC4_RECORD_SIZE == file_size
1118
+
1119
+ lo = 0
1120
+ hi = num_rec-1
1121
+ while lo <= hi:
1122
+ mid = (lo+hi)//2
1123
+ fp.seek(mid*UCAC4_RECORD_SIZE, os.SEEK_SET)
1124
+ record = fp.read(UCAC4_RECORD_SIZE_RA)
1125
+ parsed = struct.unpack(UCAC4_FMT_RA, record)
1126
+ midval = parsed[0] * MAS_TO_RAD
1127
+ if midval < ra_min:
1128
+ lo = mid+1
1129
+ elif midval > ra_min:
1130
+ hi = mid-1
1131
+ else:
1132
+ # Exact match!
1133
+ fp.seek(mid*UCAC4_RECORD_SIZE, os.SEEK_SET)
1134
+ return int(mid // UCAC4_RECORD_SIZE)
1135
+
1136
+ # At this point lo is the best we can do
1137
+ fp.seek(lo*UCAC4_RECORD_SIZE, os.SEEK_SET)
1138
+ return int(lo // UCAC4_RECORD_SIZE)