forked from Hazboun6/PulsarDataToolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpdat.py
794 lines (670 loc) · 32.4 KB
/
pdat.py
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
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
# -*- coding: utf-8 -*-
# encoding=utf8
"""Main module."""
#Pulsar Data Toolbox. Based on fitsio package. See https://github.com/esheldon/fitsio for details.
from __future__ import (absolute_import, division,
print_function, unicode_literals)
import numpy as np
import fitsio as F
import collections, os, sys
import datetime
import warnings
import six
package_path = os.path.dirname(__file__)
template_dir = os.path.join(package_path, './templates/')
class psrfits(F.FITS):
def __init__(self, psrfits_path, mode='rw', from_template=False,
obs_mode=None, verbose=True):
"""
Class which inherits fitsio.FITS() (Python wrapper for cfitsio) class's
functionality, and add's new functionality to easily manipulate and make
PSRFITS files.
Parameters
----------
from_template : bool, str
Either a boolean which dictates if a copy would like to be made from
a template, or a string which is the path to a user chosen template.
psrfits_path : str
Either the path to an existing PSRFITS file or the name for a new
file.
obs_mode : Same as OBS_MODE in a standard PSRFITS, either SEARCH, PSR or
CAL for search mode, fold mode or calibration mode respectively.
mode : str, {'r', 'rw, 'READONLY' or 'READWRITE'}
Read/Write mode.
"""
self.verbose = verbose
self.psrfits_path = psrfits_path
self.obs_mode = obs_mode
dir_path = os.path.dirname(os.path.realpath(__file__))
if os.path.exists(psrfits_path) and not from_template and verbose:
print('Loading PSRFITS file from path:\n'
' \'{0}\'.'.format(psrfits_path))
#TODO If user enters 'READWRITE' (or 'rw') but from_template=False then
# the template will track the changes in the loaded file and save them
# as using the loaded .fits as the template...
# or (from_template==False and mode='rw')
elif from_template:
if os.path.exists(psrfits_path):
os.remove(psrfits_path)
if verbose:
print('Removing older PSRFITS file from path:\n'
' \'{0}\'.'.format(psrfits_path))
if isinstance(from_template, six.string_types):
template_path = from_template
# elif isinstance(from_template, bool):
# template_path = filename #Path to template...
#TODO: Make a template that this works for
#dir_path + '/psrfits_template_' + obs_mode.lower() + '.fits'
if mode in ['r','READONLY']:
raise ValueError('Can not write new PSRFITS file if '
'it is initialized in write-only mode!')
self.written = False
self.fits_template = F.FITS(template_path, mode='r')
if self.obs_mode is None:
OBS = self.fits_template[0].read_header()['OBS_MODE'].strip()
self.obs_mode = OBS
else:
self.obs_mode = obs_mode
self.draft_hdrs = collections.OrderedDict()
self.HDU_drafts = {}
self.subint_dtype = None
#Set the ImageHDU to be called primary.
self.draft_hdrs['PRIMARY'] = self.fits_template[0].read_header()
self.n_hdrs = len(self.fits_template.hdu_list)
for ii in np.arange(1,self.n_hdrs):
hdr_key = self.fits_template[ii].get_extname()
self.draft_hdrs[hdr_key] = self.fits_template[ii].read_header()
self.HDU_drafts[hdr_key] = None
self.draft_hdr_keys = list(self.draft_hdrs.keys())
if verbose:
msg = 'Making new {0} mode PSRFITS file '.format(self.obs_mode)
msg += 'using template from path:\n'
msg += ' \'{0}\'. \n'.format(template_path)
msg += 'Writing to path: \n \'{0}\''.format(psrfits_path)
print(msg)
if sys.version_info[0]<3:
try:
super(psrfits, self).__init__(psrfits_path, mode = mode)
except TypeError:
err_msg = 'Python 2 sometimes errors when reloading in '
err_msg += 'Jupyter Notebooks. Try reloading kernel.'
raise TypeError(err_msg)
else:
super().__init__(psrfits_path, mode = mode)
#If self.obs_mode is still None use loaded PSRFITS file
if self.obs_mode is None and from_template:
OBS = self.fits_template[0].read_header()['OBS_MODE'].strip()
self.obs_mode = OBS
if from_template and verbose:
print('The Binary Table HDU headers will be written as '
'they are added\n to the PSRFITS file.')
elif not from_template and (mode=='rw' or mode=='READWRITE'):
self.draft_hdrs = collections.OrderedDict()
self.HDU_drafts = {}
#Set the ImageHDU to be called primary.
self.draft_hdrs['PRIMARY'] = self[0].read_header()
self.n_hdrs = len(self.hdu_list)
self.written = False
for ii in range(self.n_hdrs-1):
hdr_key = self[ii+1].get_extname()
self.draft_hdrs[hdr_key] = self[ii+1].read_header()
self.HDU_drafts[hdr_key] = None
self.draft_hdr_keys = list(self.draft_hdrs.keys())
def write_psrfits(self, HDUs=None, hdr_from_draft=True):
"""
Function that takes the template headers and a dictionary of recarrays
to make into PSRFITS HDU's. These should only include BinTable HDU
Extensions, not the PRIMARY header (an ImageHDU). PRIMARY is dealt
with a bit differently.
Parameters
----------
HDUs : dict, optional
Dictionary of recarrays to make into HDUs. Default is set to
HDU_drafts
"""
if self.written:
raise ValueError('PSRFITS file has already been written. '
'Can not write twice.')
if HDUs is None:
HDUs = self.HDU_drafts
if any([val is None for val in HDUs.values()]):
raise ValueError('One of HDU drafts is \"None\".')
self.write_PrimaryHDU_info_dict(self.fits_template[0],self[0])
self.set_hdr_from_draft('PRIMARY')
for hdr in self.draft_hdr_keys[1:]:
self.write_table(HDUs[hdr],extname=hdr, extver=1)
# header = self.draft_hdrs[hdr])
if hdr_from_draft: self.set_hdr_from_draft(hdr)
self.written = True
# def write_psrfits_from_draft?(self):
# self.write_PrimaryHDU_info_dict(self.fits_template[0],self[0])
# self.set_hdr_from_draft('PRIMARY')
# #Might need to go into for loop if not true for all BinTables
# nrows = self.draft_hdrs['SUBINT']['NAXIS2']
# for jj, hdr in enumerate(self.draft_hdr_keys[1:]):
# HDU_dtype_list = self.get_HDU_dtypes(self.fits_template[jj+1])
# rec_array = self.make_HDU_rec_array(nrows, HDU_dtype_list)
# self.write_table(rec_array)
# self.set_hdr_from_draft(hdr)
# def append_subint_array(self,table):
# """
# Method to append more subintegrations to a PSRFITS file from Python
# arrays.
# The array must match the columns (in the numpy.recarray sense)
# of the existing PSRFITS file.
# """
# fits_to_append = F.FITS(table)
def append_from_file(self,path,table='all'):
"""
Method to append more subintegrations to a PSRFITS file from other
PSRFITS files.
Note: Tables are appended directly to the original file. Make a copy
before copying if you are unsure about appending. The array must
match the columns (in the numpy.recarray sense) of the existing
PSRFITS file.
Parameters
----------
path : str
Path to the new PSRFITS file to be appended.
table : list
List of BinTable HDU headers to append from file. Defaults to
appending all secondary BinTables.
['HISTORY','PSRPARAM','POLYCO','SUBINT']
"""
PF2A = F.FITS(path, mode='r')
PF2A_hdrs = []
PF2A_hdrs.append('PRIMARY')
for ii in range(self.n_hdrs-1):
hdr_key = PF2A[ii+1].get_extname()
PF2A_hdrs.append(hdr_key)
if table=='all':
if PF2A_hdrs!= self.draft_hdr_keys:
if len(PF2A_hdrs)!= self.n_hdrs:
err_msg = '{0} and {1} do '.format(self.psrfits_path,path)
err_msg += 'not have the same number of BinTable HDUs.'
raise ValueError(err_msg)
else:
err_msg = 'Original PSRFITS HDUs'
err_msg = ' ({0}) and PSRFITS'.format(self.draft_hdr_keys)
err_msg = ' to append ({1})'.format(PF2A_hdrs)
err_msg = ' have different BinTables or they are in'
err_msg = ' different orders. \nEnter a table list matching'
err_msg = ' the order of the orginal PSRFITS file.'
raise ValueError(err_msg)
else:
table=PF2A_hdrs
for hdr in self.draft_hdr_keys[1:]:
rec_array = PF2A[list_arg(table,hdr)].read()
self[list_arg(self.draft_hdr_keys,hdr)].append(rec_array)
#######Convenience Functions################
def get_colnames():
"""Returns the names of all of the columns of data needed for a PSRFITS
file."""
return self[1].get_colnames()
def set_hdr_from_draft(self, hdr):
"""Sets a header of the PSRFITS file using the draft header derived from
template."""
keys = self.draft_hdr_keys
if isinstance(hdr,int):
hdr_name = keys[hdr]
if isinstance(hdr, six.string_types):
hdr_name = hdr.upper()
hdr = list_arg(keys,hdr_name)
# with warnings.catch_warnings(): #This is very Dangerous
# warnings.simplefilter("ignore")
for card in self.draft_hdrs[hdr_name].records():
card = convert2asciii(card)
self[hdr].write_keys(self.draft_hdrs[hdr_name],clean=False)
#Must set clean to False or the first keys are deleted!
def get_FITS_card_dict(self, hdr, name):
"""
Make a FITS card compatible dictionary from a template FITS header that
matches the input name key in a standard FITS card/record. It is
necessary to make a new FITS card/record to change values in the header.
This function outputs a writeable dictionary which can then be used to
change the value in the header using the hdr.add_record() method.
Parameters
----------
hdr : fitsio.fitslib.FITSHDR object
Template for the card.
name : str
The name key in the FITS record you wish to make.
"""
card = next((item for item in hdr.records()
if item['name'] == name.upper()), False)
if not card:
err_msg = 'A FITS card named '
err_msg += '{0} does not exist in this HDU.'.format(name)
raise ValueError(err_msg)
return card
def make_FITS_card(self, hdr, name, new_value):
"""
Make a new FITS card/record using a FITS header as a template.
This function makes a new card by finding the card/record in the
template with the same name and replacing the value with new_value.
Note: fitsio will set the dtype dependent on the form of the new_value
for numbers.
Parameters
----------
hdr : fitsio.fitslib.FITSHDR
A fitsio.fitslib.FITSHDR object, which acts as the template.
name : str
A string that matches the name key in the FITS record you wish to
make.
new_value : str, float
The new value you would like to replace.
"""
record = self.get_FITS_card_dict(hdr,name)
record_value = record['value']
dtype = record['dtype']
string_dtypes = ['C']
number_dtypes = ['I','F']
def _fits_format(new_value,record_value):
"""
Take in the new_value and record value, and format for searching
card string. Change the shape of the string to fill out PSRFITS
File Correctly.
"""
try: #when new_value is a string
if len(new_value)<=len(record_value):
str_len = len(record_value)
new_value = new_value.ljust(str_len)
card_string = record['card_string'].replace(record_value,
new_value)
except TypeError: # When new_value is a number
old_val_str = str(record_value)
old_str_len = len(old_val_str)
new_value = str(new_value)
new_str_len = len(new_value)
if new_str_len < old_str_len:
# If new value is shorter fill out with spaces.
new_value = new_value.rjust(old_str_len)
elif new_str_len > old_str_len:
if new_str_len>20:
new_value=new_value[:20]
new_str_len = 20
# If new value is longer pull out more spaces.
old_val_str = old_val_str.rjust(new_str_len)
card_string = record['card_string'].replace(old_val_str,
new_value)
return card_string
def _replace_center_of_cardstring(new_value):
"""
Replaces the entire center of the card string using the new value.
"""
cardstring = record['card_string']
equal_idx = old_cardstring.find('=')
slash_idx = old_cardstring.find('/')
len_center = slash_idx - equal_idx - 1
new_center = str(new_value).rjust(len_center)
cardstring[equal_idx+1, slash_idx] = new_center
return cardstring
#if isinstance(record['value'],tuple):
# record['value'] = str(record['value']).replace(' ','')
# for TDIM17, TDIM20 in SUBINT HDU...
# Could make more specific if necessary.
special_fields = ['TDIM17','TDIM20']
if record['name'] in special_fields:
new_record = record
record_value = str(record_value).replace(' ','')
card_string = _fits_format(new_value.replace(' ',''), record_value)
new_record['card_string'] = card_string.replace('\' (','\'(')
new_record['value'] = new_value
new_record['value_orig'] = new_record['value']
#TODO Add error checking new value... and isinstance(new_value)
#Usual Case
elif str(record['value']) in record['card_string']:
card_string = _fits_format(new_value, record_value)
new_record = F.FITSRecord(card_string)
#Special Case 1, Find Numbers with trailing zeros and writes string.
elif ((str(record['value'])[-1]=='0')
and (str(record['value'])[:-1] in record['card_string'])):
record_value = str(record['value'])[:-1]
#Adds decimal pt to end of string.
if record_value[-1]=='.' and str(new_value)[-1]!='.':
new_value = str(new_value) + '.'
card_string = _fits_format(new_value, record_value)
new_record = F.FITSRecord(card_string)
#Special Case 2, Find Numbers with upper/lower E in sci notation
#that do not match exactly. Always use E in card string.
elif (('dtype' in record.keys())
and (record['dtype'] in number_dtypes)
and (('E' in str(record_value)) or ('e' in str(record_value))
or ('E' in str(record['value_orig']))
or ('e' in str(record['value_orig'])))):
new_value = str(new_value).upper()
if str(record_value).upper() in record['card_string']:
record_value = str(record_value).upper()
card_string = _fits_format(new_value, record_value)
new_record = F.FITSRecord(card_string)
elif str(record_value).lower() in record['card_string']:
record_value =str(record_value).lower()
card_string = _fits_format(new_value, record_value)
new_record = F.FITSRecord(card_string)
else:
card_string = _replace_center_of_cardstring(new_value)
new_record = F.FITSRecord(card_string)
msg = 'Old value cannot be found in card string. '
msg += 'Entire center replaced.'
print(msg)
#Replace whole center if can't find value.
else:
card_string = _replace_center_of_cardstring(new_value)
new_record = F.FITSRecord(card_string)
msg = 'Old value cannot be found in card string. '
msg += 'Entire center replaced.'
print(msg)
if new_record['value'] != new_record['value_orig']:
new_record['value_orig'] = new_record['value']
return new_record
def replace_FITS_Record(self, hdr, name, new_value):
"""
Replace a Fits record with a new value in a fitsio.fitslib.FITSHDR
object.
Parameters
----------
hdr : str or FITSHDR object
Header name.
name : FITS Record/Car
FITS Record/Card name to replace.
new_value : float, str
The new value of the parameter.
"""
# try:
# new_record = self.make_FITS_card(hdr,name,new_value)
# except AttributeError:
#
# new_record = self.make_FITS_card(hdr,name,new_value)
if not isinstance(hdr,F.fitslib.FITSHDR):
hdr = self.draft_hdrs[hdr]
#Maybe faster if try: except: used?
new_record = self.make_FITS_card(hdr,name,new_value)
hdr.add_record(new_record)
def get_HDU_dtypes(self, HDU):
"""
Returns a list of data types and array sizes needed to make a recarray.
HDU = A FITS HDU.
"""
return HDU.get_rec_dtype()[0].descr
def set_HDU_array_shape_and_dtype(self, HDU_dtype_list, name,
new_array_shape=None, new_dtype=None):
"""
Takes a list of data types (output of get_HDU_dtypes()) and returns new
list with the named element's array shape and/or data type edited.
Parameters
----------
HDU_dtype_list :
dtype list for making recarray (output of get_HDU_dtypes()).
name : str
Name of parameter to edit.
new_array_shape : tuple
New array shape. Note 1-d arrays are of type (n,) in FITS files.
new_dtype :
New data type. See PSRFITS and fitsio documentation for recognized
names.
"""
try:
ii = [x for x, y in enumerate(HDU_dtype_list)
if y[0] == name.upper()][0]
except:
err_msg = 'The name \'{0}\' is not '.format(name)
err_msg += 'in the given HDU dtype list.'
raise ValueError(err_msg)
if new_dtype and new_array_shape:
HDU_dtype_list[ii] = (HDU_dtype_list[ii][0],new_dtype,
new_array_shape)
elif new_array_shape:
HDU_dtype_list[ii] = (HDU_dtype_list[ii][0],HDU_dtype_list[ii][1],
new_array_shape)
elif new_dtype:
HDU_dtype_list[ii] = (HDU_dtype_list[ii][0],new_dtype,
HDU_dtype_list[ii][2])
def make_HDU_rec_array(self, nrows, HDU_dtype_list):
"""
Makes a rec array with the set number of rows and data structure
dictated by the dtype list.
"""
#TODO Add in hdf5 type file format for large arrays?
return np.empty(nrows, dtype=HDU_dtype_list)
def write_PrimaryHDU_info_dict(self, ImHDU_template, new_ImHDU):
"""
Writes the information dictionary for a primary header Image HDU
(new_ImHDU) using ImHDU_template as the template. Both are FITS HDUs.
Parameters
----------
ImHDU_template :
Template header.
new_ImHDU :
Header where template is copied.
"""
templ_info_keys = list(ImHDU_template.__dict__['_info'].keys())
new_info_keys = list(new_ImHDU.__dict__['_info'].keys())
info_keys = np.unique(np.concatenate((templ_info_keys,new_info_keys)))
for key in info_keys:
if key in templ_info_keys:
new_ImHDU.__dict__['_info'][key] = ImHDU_template.__dict__['_info'][key]
elif key not in templ_info_keys:
new_ImHDU.__dict__['_info'].__delitem__(key)
def set_subint_dims(self, nbin=1, nchan=2048, npol=4, nsblk=4096,
nsubint=4, obs_mode=None, data_dtype='|u1'):
"""
Method to set the appropriate parameters for the SUBINT BinTable of
a PSRFITS file of the given dimensions.
The parameters above are defined in the PSRFITS literature.
The method automatically changes all the header information in the
template dependent on these values. The header template is set to
these values.
A list version of a dtype array is made which has all the info needed
to make a SUBINT recarray. This can then be written to a PSRFITS file,
using the command write_prsfits().
Parameters
----------
nbin : int
NBIN, number of bins. 1 for SEARCH mode data.
nchan : int
NCHAN, number of frequency channels.
npol : int
NPOL, number of polarization channels.
nsblk : int
NSBLK, size of the data chunks for search mode data. Set to 1 for
PSR and CAL mode.
nsubint : int
NSUBINT or NAXIS2 . This is the number of rows or subintegrations
in the PSRFITS file.
obs_mode : str , {'SEARCH', 'PSR', 'CAL'}
Observation mode.
data_type : str
Data type of the DATA array ('|u1'=int8 or '|u2'=int16).
"""
self.nrows = self.nsubint = nsubint
#Make a dtype list with defined dimensions and data type
self._bytes_per_datum = np.dtype(data_dtype).itemsize
if obs_mode is None: obs_mode = self.obs_mode
if obs_mode.upper() == 'SEARCH':
self.subint_idx = self.draft_hdr_keys.index('SUBINT')
if nbin != 1:
err_msg = 'NBIN (set to {0}) parameter not set '.format(nbin)
err_msg += 'to correct value for SEARCH mode.'
raise ValueError(err_msg)
self.nbits = 8 * self._bytes_per_datum
#Set Header values dependent on data shape
self.replace_FITS_Record('PRIMARY','BITPIX',8)
self.replace_FITS_Record('SUBINT','BITPIX',8)
self.replace_FITS_Record('SUBINT','NBITS',self.nbits)
self.replace_FITS_Record('SUBINT','NBIN',nbin)
self.replace_FITS_Record('SUBINT','NCHAN',nchan)
self.replace_FITS_Record('PRIMARY','OBSNCHAN',nchan)
self.replace_FITS_Record('SUBINT','NPOL',npol)
self.replace_FITS_Record('SUBINT','NSBLK',nsblk)
self.replace_FITS_Record('SUBINT','NAXIS2',nsubint)
self.replace_FITS_Record('SUBINT','TFORM13',str(nchan)+'E')
self.replace_FITS_Record('SUBINT','TFORM14',str(nchan)+'E')
self.replace_FITS_Record('SUBINT','TFORM15',str(nchan*npol)+'E')
self.replace_FITS_Record('SUBINT','TFORM16',str(nchan*npol)+'E')
#Calculate Number of Bytes in each row's DATA array
tform17 = nbin*nchan*npol*nsblk
self.replace_FITS_Record('SUBINT','TFORM17',str(tform17)+'B')
#This is the number of bytes in TSUBINT, OFFS_SUB, LST_SUB, etc.
bytes_in_lone_floats = 7*8 + 5*4
naxis1 = tform17*self._bytes_per_datum + 2*nchan*4 + 2*nchan*npol*4
naxis1 += bytes_in_lone_floats
self.replace_FITS_Record('SUBINT','NAXIS1', str(naxis1))
# Set the TDIM17 string-tuple
tdim17 = '('+str(nbin)+', '+str(nchan)+', '
tdim17 += str(npol)+', '+str(nsblk)+')'
self.replace_FITS_Record('SUBINT','TDIM17', tdim17)
self.subint_dtype = self.get_HDU_dtypes(self.fits_template
[self.subint_idx])
self.set_HDU_array_shape_and_dtype(self.subint_dtype,'DATA',
(nbin,nchan,npol,nsblk))
#,data_dtype)
self.single_subint_floats=['TSUBINT','OFFS_SUB',
'LST_SUB','RA_SUB',
'DEC_SUB','GLON_SUB',
'GLAT_SUB','FD_ANG',
'POS_ANG','PAR_ANG',
'TEL_AZ','TEL_ZEN']
elif (obs_mode.upper() == 'PSR' or obs_mode.upper() == 'CAL'):
self.subint_idx = self.draft_hdr_keys.index('SUBINT')
if nsblk != 1:
err_msg = 'NSBLK (set to {0}) parameter not set '.format(nsblk)
err_msg += 'to correct value '
err_msg += 'for {0} mode.'.format(obs_mode.upper())
raise ValueError(err_msg)
self.nbits = 1
self.replace_FITS_Record('PRIMARY','BITPIX',8)
self.replace_FITS_Record('SUBINT','BITPIX',8)
self.replace_FITS_Record('SUBINT','NBITS',self.nbits)
self.replace_FITS_Record('SUBINT','NBIN',nbin)
self.replace_FITS_Record('SUBINT','NCHAN',nchan)
self.replace_FITS_Record('PRIMARY','OBSNCHAN',nchan)
self.replace_FITS_Record('SUBINT','NPOL',npol)
self.replace_FITS_Record('SUBINT','NSBLK',nsblk)
self.replace_FITS_Record('SUBINT','NAXIS2',nsubint)
self.replace_FITS_Record('SUBINT','TFORM16',str(nchan)+'D')
self.replace_FITS_Record('SUBINT','TFORM17',str(nchan)+'E')
self.replace_FITS_Record('SUBINT','TFORM18',str(nchan*npol)+'E')
self.replace_FITS_Record('SUBINT','TFORM19',str(nchan*npol)+'E')
#Calculate Number of Bytes in each row's DATA array
tform20 = nbin*nchan*npol
self.replace_FITS_Record('SUBINT','TFORM20',str(tform20)+'I')
bytes_in_lone_floats = 10*8 + 5*4
#This is the number of bytes in TSUBINT, OFFS_SUB, LST_SUB, etc.
naxis1 = tform20*self._bytes_per_datum + nchan*8 + nchan*4
naxis1 += 2*nchan*npol*4 + bytes_in_lone_floats
self.replace_FITS_Record('SUBINT','NAXIS1', str(naxis1))
# Set the TDIM20 string-tuple
tdim20 = '('+str(nbin)+', '+str(nchan)+', ' + str(npol)+')'
self.replace_FITS_Record('SUBINT','TDIM20', tdim20)
self.subint_dtype = self.get_HDU_dtypes(self.fits_template
[self.subint_idx])
self.set_HDU_array_shape_and_dtype(self.subint_dtype,'DATA',
(npol,nchan,nbin))
self.single_subint_floats=['TSUBINT','OFFS_SUB',
'LST_SUB','RA_SUB',
'DEC_SUB','GLON_SUB',
'GLAT_SUB','FD_ANG',
'POS_ANG','PAR_ANG',
'TEL_AZ','TEL_ZEN',
'AUX_DM','AUX_RM']
self.set_HDU_array_shape_and_dtype(self.subint_dtype,
'DAT_FREQ',(nchan,))
self.set_HDU_array_shape_and_dtype(self.subint_dtype,
'DAT_WTS',(nchan,))
self.set_HDU_array_shape_and_dtype(self.subint_dtype,
'DAT_OFFS',(nchan*npol,))
self.set_HDU_array_shape_and_dtype(self.subint_dtype,
'DAT_SCL',(nchan*npol,))
def copy_template_BinTable(self, ext_name, cols='all', dtypes=None):
"""
Method to copy PSRFITS binary tables exactly. This is
especially useful when using real PSRFITS files to make simulated
data, i.e. if you would just like to replace the DATA arrays in the
file with your simulated data, but keep the ancillary telescope
information. This copies the BinTable as a numpy.recarray into the
`HDU_drafts` dictionary.
Parameters
----------
ext_name : str, {'PRIMARY','SUBINT','HISTORY','PSRPARAM','POLYCO'}
Binary Extension name to copy.
cols : str or list
Columns of the given BinTable to copy.
dtypes : list of tuples
Data types for numpy.recarray that will be the draft for the
BinTable.
"""
idx = self.draft_hdr_keys.index(ext_name)
dtypes = self.get_HDU_dtypes(self.fits_template[idx])
nrows = self.fits_template[idx].read_header()['NAXIS2']
if cols=='all':
cols = self.fits_template[idx].get_colnames()
self.HDU_drafts[ext_name] = self.make_HDU_rec_array(nrows, dtypes)
copy_cols = self.fits_template[idx].read(columns=cols)
#TODO Think about how this would change for appending single rows.
for col in cols:
self.HDU_drafts[ext_name][col][:] = copy_cols[col][:]
del copy_cols
def set_draft_header(self, ext_name, hdr_dict):
"""
Set draft header entries for the new PSRFITS file from a dictionary.
Parameters
----------
psrfits_object : pdat.psrfits
Pulsar Data Toolbox PSRFITS object.
ext_name : str, {'PRIMARY','SUBINT','HISTORY','PSRPARAM','POLYCO'}
Name of the header to replace the header entries.
hdr_dict : dict
Dictionary of header changes to be made to the template header.
Template header entries are kept, unless replaced by this function.
"""
for key in hdr_dict.keys():
self.replace_FITS_Record(ext_name,key,hdr_dict[key])
def close(self):
"""
Override of fitsio close method. Adds more variables to set to none.
Close the fits file and set relevant metadata to None
"""
if hasattr(self,'_FITS'):
if self._FITS:
#if self.verbose:
# print('PSRFITS file is closing.')
#This call to print prevents a stack overflow.I do not know why.
print('')
self._FITS.close()
self._FITS=None
self._filename=None
self.mode=None
self.charmode=None
self.intmode=None
#TODO Write script that sets all non overlapping variables to None.
self.HDU_drafts=None
self.draft_hdr_keys=None
self.draft_hdrs=None
self.n_hdrs=None
self.psrfits_path=None
self.hdu_list=None
self.hdu_map=None
# def real_data(self):
# """
# Method that reads the DATA, DAT_SCL, DAT_OFFS and DAT_WTS together
# into a HDF5 file so that the real data can be used for
# calculations.
# """
# data = h5py.File('data.hdf5',mode=w)
# data = self[1].read_columns('DATA')
# dat_offs = self[1].read_columns('DATA_OFFS')
# dat_scl = self[1].read_columns('DATA_SCL')
# dat_wts = self[1].read_columns('DATA_OFFS')
# #data = DATA*DAT_SCL+DAT_OFFS
def list_arg(list_name, string):
"""Returns the index of a particular string in a list of strings."""
return [x for x, y in enumerate(list_name) if y == string][0]
def convert2asciii(dictionary):
"""
Changes all keys (i.e. assumes they are strings) to ASCII and
values that are strings to ASCII. Specific to dictionaries.
"""
return dict([(key.encode('ascii','ignore'),value.encode('ascii','ignore'))
if type(value) in [str,bytes] else
(key.encode('ascii','ignore'),value)
for key, value in dictionary.items()])