initial project code commit
This commit is contained in:
BIN
artpipeline/.DS_Store
vendored
Normal file
BIN
artpipeline/.DS_Store
vendored
Normal file
Binary file not shown.
6
artpipeline/pyproject.toml
Normal file
6
artpipeline/pyproject.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
17
artpipeline/setup.py
Normal file
17
artpipeline/setup.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from setuptools import setup
|
||||
|
||||
exec(open('src/artpipeline/version.py').read())
|
||||
|
||||
setup(
|
||||
name="artpipeline",
|
||||
version=__version__,
|
||||
author="M.Pavlinsky SRG/ART-XC software team",
|
||||
description="data reduction pipeline",
|
||||
package_dir={"": "src"},
|
||||
packages=["artpipeline"],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"artpipeline = artpipeline.pipeline:main",
|
||||
]
|
||||
},
|
||||
)
|
||||
BIN
artpipeline/src/.DS_Store
vendored
Normal file
BIN
artpipeline/src/.DS_Store
vendored
Normal file
Binary file not shown.
325
artpipeline/src/artpipeline/caldb.py
Normal file
325
artpipeline/src/artpipeline/caldb.py
Normal file
@@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2020-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime as dt
|
||||
|
||||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
import polars as pl
|
||||
|
||||
from astropy.io import fits
|
||||
# from astropy.table import Table
|
||||
|
||||
from arttools.datatable import DataTable, FitsAdapter
|
||||
|
||||
|
||||
# if 'ARTCALDB' in os.environ:
|
||||
# ARTCALDBPATH = os.getenv('ARTCALDB')
|
||||
# else:
|
||||
# print('error: ARTCALDB environment variable not found')
|
||||
# sys.exit(-1)
|
||||
|
||||
|
||||
class CaldbInfo(object):
|
||||
|
||||
def __init__(self, caldbdir):
|
||||
self._caldbpath = caldbdir
|
||||
self._indexfile = 'caldb.indx'
|
||||
self._caldbindex = os.path.join(self._caldbpath, self._indexfile)
|
||||
|
||||
with fits.open(self._caldbindex) as caldbfile:
|
||||
self._version = caldbfile['CIF'].header['ARTCALDB']
|
||||
# self._index = Table(caldbfile['CIF'].data).to_pandas()
|
||||
|
||||
self._index = DataTable().adapter(FitsAdapter(self._caldbindex, 'CIF'))
|
||||
self._index.read()
|
||||
|
||||
def get(self):
|
||||
return {
|
||||
'CALDBDIR': self._caldbpath,
|
||||
'CALDBVER': self._version
|
||||
}
|
||||
|
||||
|
||||
class Caldb(object):
|
||||
|
||||
def __init__(self, caldbdir, instname, silent=True):
|
||||
|
||||
self._instname = instname
|
||||
self._silent = silent
|
||||
self._version = None
|
||||
self._index = None
|
||||
|
||||
# --- read version, index
|
||||
self._caldbpath = caldbdir
|
||||
self._indexfile = 'caldb.indx'
|
||||
self._caldbindex = os.path.join(self._caldbpath, self._indexfile)
|
||||
|
||||
with fits.open(self._caldbindex) as caldbfile:
|
||||
self._version = caldbfile['CIF'].header['ARTCALDB']
|
||||
# self._index = Table(caldbfile['CIF'].data).to_pandas()
|
||||
|
||||
self._index = DataTable().adapter(FitsAdapter(self._caldbindex, 'CIF'))
|
||||
self._index.read()
|
||||
|
||||
# print('CALDB: version {} [{}]'.format(self._version, self._caldbfilespath))
|
||||
|
||||
@property
|
||||
def silent(self):
|
||||
return self._silent
|
||||
|
||||
def cif(self, instname, calcname):
|
||||
iname = instname; cname = calcname
|
||||
|
||||
if not self._silent:
|
||||
print(self._silent)
|
||||
print('CALDB: select: {}'.format(
|
||||
'INSTRUME==\'{}\' and CAL_CNAM==\'{}\''.format(iname, cname)))
|
||||
|
||||
# iname = iname.ljust(10, ' ')
|
||||
# cname = cname.ljust(20, ' ')
|
||||
|
||||
# return self._index.query(
|
||||
# 'INSTRUME==\'{}\' and CAL_CNAM==\'{}\''.format(iname, cname))
|
||||
|
||||
iname = iname.ljust(10, ' ')
|
||||
cname = cname.ljust(20, ' ')
|
||||
|
||||
return self._index.frame.filter(
|
||||
pl.col('INSTRUME') == iname,
|
||||
pl.col('CAL_CNAM') == cname
|
||||
)
|
||||
|
||||
def file(self, calcname):
|
||||
return self.instfile(self._instname, calcname)
|
||||
|
||||
def instfile(self, instname, calcname):
|
||||
selected = self.cif(instname, calcname)
|
||||
for cdbpath, cdbfile in zip(selected['CAL_DIR'], selected['CAL_FILE']):
|
||||
cdbpath = cdbpath.strip()
|
||||
cdbfile = cdbfile.strip()
|
||||
|
||||
if not self._silent:
|
||||
print('CALDB: product: {}'.format(cdbfile))
|
||||
|
||||
p = Path(cdbpath)
|
||||
p = Path(*p.parts[2:]) # chop off data/artxc_caldb
|
||||
|
||||
cdbpath = p
|
||||
|
||||
# return first record only, @fixme
|
||||
return os.path.join(cdbpath, cdbfile)
|
||||
|
||||
return None
|
||||
|
||||
def path(self, folder='bcf'):
|
||||
return os.path.join(self._caldbpath, folder)
|
||||
|
||||
def instname(self):
|
||||
return self._instname
|
||||
|
||||
def version(self):
|
||||
return self._version
|
||||
|
||||
|
||||
class CaldbTelescope(object):
|
||||
|
||||
def __init__(self, caldb):
|
||||
self._caldb = caldb
|
||||
self._silent = self._caldb.silent
|
||||
self._oaxis_file \
|
||||
= os.path.join(self._caldb.path(), self._caldb.file('OPTAXIS'))
|
||||
|
||||
with fits.open(self._oaxis_file) as hdul:
|
||||
self._oaxis = {
|
||||
'X': hdul[1].data['X'][0],
|
||||
'Y': hdul[1].data['Y'][0]
|
||||
}
|
||||
|
||||
if not self._silent:
|
||||
print('CALDB: selected OPTAXIS: X={} Y={}'.format(self._oaxis['X'], self._oaxis['Y']))
|
||||
|
||||
def oaxis(self):
|
||||
return self._oaxis
|
||||
|
||||
def info(self):
|
||||
meta = {
|
||||
'OPTAXIS': self._oaxis
|
||||
}
|
||||
|
||||
|
||||
# class CaldbEnergy(object):
|
||||
|
||||
# def __init__(self, instname):
|
||||
# self._caldb = Caldb(instname)
|
||||
|
||||
# self._thresh_file \
|
||||
# = self._caldb.file('THRESHOL')
|
||||
# self._escale_file \
|
||||
# = self._caldb.file('ESCALE' )
|
||||
|
||||
# #todo: check files is not None
|
||||
|
||||
# self._thresh = fits.open(os.path.join(self._caldb.path(), self._thresh_file))
|
||||
# self._escale = fits.open(os.path.join(self._caldb.path(), self._escale_file))
|
||||
|
||||
# def bot(self):
|
||||
# return self._thresh['BOT'].data
|
||||
|
||||
# def top(self):
|
||||
# return self._thresh['TOP'].data
|
||||
|
||||
# def scale(self):
|
||||
# # escale_data = self._escale['ESCALE'].data
|
||||
# escale_data = self._escale['E_COEFF'].data
|
||||
# obt_data = escale_data['OBT']
|
||||
# c0_data = escale_data['C0' ]
|
||||
# c1_data = escale_data['C1' ]
|
||||
|
||||
# scale_fn = interp1d(obt_data, c0_data, bounds_error=False, fill_value=tuple(c0_data[[0, -1]]))
|
||||
# shift_fn = interp1d(obt_data, c1_data, bounds_error=False, fill_value=tuple(c1_data[[0, -1]]))
|
||||
|
||||
# # print last time (obt_data)
|
||||
# # save it to header
|
||||
|
||||
# return {
|
||||
# 'C0': scale_fn,
|
||||
# 'C1': shift_fn}
|
||||
|
||||
# def version(self):
|
||||
# return self._caldb.version()
|
||||
|
||||
# def info(self):
|
||||
# meta = {
|
||||
# 'THRESHOL': self._thresh_file,
|
||||
# 'ESCALE' : self._escale_file,
|
||||
# 'CALDBVER': self.version()
|
||||
# }
|
||||
# return meta
|
||||
|
||||
class CaldbEnergy(object):
|
||||
|
||||
def __init__(self, caldbdir, instname):
|
||||
self._caldb = Caldb(caldbdir, instname)
|
||||
|
||||
self._chen_file \
|
||||
= self._caldb.file('CHENERGY')
|
||||
|
||||
self._chen = fits.open(os.path.join(self._caldb.path(), self._chen_file))
|
||||
|
||||
def bot(self):
|
||||
return self._chen['BOT'].data
|
||||
|
||||
def top(self):
|
||||
return self._chen['TOP'].data
|
||||
|
||||
def version(self):
|
||||
return self._caldb.version()
|
||||
|
||||
def info(self):
|
||||
meta = {
|
||||
'CHENERGY': self._chen_file,
|
||||
'CALDBVER': self.version()
|
||||
}
|
||||
return meta
|
||||
|
||||
|
||||
class CaldbMask(object):
|
||||
|
||||
def __init__(self, caldbdir, instname):
|
||||
self._caldb = Caldb(caldbdir, instname)
|
||||
|
||||
self._dmask_file \
|
||||
= os.path.join(self._caldb.path(), self._caldb.file('DETMASK'))
|
||||
self._dmask = np.copy(fits.getdata(self._dmask_file, 1)).astype(bool).T
|
||||
|
||||
# self._dmask = fits.open(os.path.join(self._caldb.path(), self._dmask_file))
|
||||
|
||||
def get(self):
|
||||
return self._dmask
|
||||
|
||||
def version(self):
|
||||
return self._caldb.version()
|
||||
|
||||
def info(self):
|
||||
meta = {
|
||||
'DETMASK': self._dmask_file,
|
||||
'CALDBVER': self.version()
|
||||
}
|
||||
return meta
|
||||
|
||||
|
||||
class CaldbOrientation(object):
|
||||
|
||||
_INST_QUAT = {
|
||||
'GYRO': [ 0. , 0. , 0. , 1. ],
|
||||
'BOKZ': [ 0. , -0.707106781186548, 0. , 0.707106781186548],
|
||||
'SED1': [ 0.183012701892219, 0.683012701892219, -0.183012701892219, 0.683012701892219],
|
||||
'SED2': [-0.183012701892219, 0.683012701892219, 0.183012701892219, 0.683012701892219]}
|
||||
|
||||
def __init__(self, caldbdir, instname):
|
||||
self._delay_file = None
|
||||
self._bs_file = None
|
||||
self._caldb = Caldb(caldbdir, instname)
|
||||
self._delay = self.read_time_delay()
|
||||
self._instquat = self.read_instquat ()
|
||||
self._boresight = self.read_boresight ()
|
||||
|
||||
def read_time_delay(self):
|
||||
delay_file = self._caldb.file('{instname}LAG'.format(instname=self._caldb.instname()))
|
||||
delay_hdul = fits.open(os.path.join(self._caldb.path(), delay_file))
|
||||
delay = delay_hdul[1].data['DELAY'][0]
|
||||
|
||||
self._delay_file = delay_file
|
||||
|
||||
# print('CALDB: select {} DELAY={}'.format(self._caldb.instname(), delay))
|
||||
|
||||
return delay
|
||||
|
||||
def read_instquat(self):
|
||||
return self._INST_QUAT[self._caldb.instname()]
|
||||
|
||||
def read_boresight(self, instname=None):
|
||||
if instname is None:
|
||||
instname = self._caldb.instname()
|
||||
|
||||
bs_file = self._caldb.instfile(instname, 'BORESIGH')
|
||||
bs_hdul = fits.open(os.path.join(self._caldb.path(), bs_file))
|
||||
|
||||
self._bs_file = bs_file
|
||||
|
||||
return bs_hdul[1].data[0]
|
||||
|
||||
def timeshift(self):
|
||||
return self._delay
|
||||
|
||||
def instquat(self):
|
||||
return self._instquat
|
||||
|
||||
def boresight(self):
|
||||
return self._boresight
|
||||
|
||||
def version(self):
|
||||
return self._caldb.version()
|
||||
|
||||
def info(self):
|
||||
meta = {
|
||||
'INSTRUME': self._caldb.instname(),
|
||||
'TIMELAG' : self._delay_file,
|
||||
'BORESIGH': self._bs_file
|
||||
}
|
||||
return meta
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
152
artpipeline/src/artpipeline/energy.old.py
Normal file
152
artpipeline/src/artpipeline/energy.old.py
Normal file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2020-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
from .time import Mission
|
||||
|
||||
|
||||
class Energy(object):
|
||||
|
||||
def __init__(self, caldb_energy, seed=None):
|
||||
self._caldb_energy = caldb_energy
|
||||
self._seed = seed
|
||||
|
||||
self.GRADES = np.ones(256) * (-1)
|
||||
self.GRADES[18] = 0 # single central events
|
||||
self.GRADES[[50, 26, 22, 19, 54, 51, 30, 27]] = [1, 2, 3, 4, 5, 6, 7, 8] # double events
|
||||
self.GRADES[[58, 62, 59, 23, 55, 31, 63]] = [9, 10, 11, 12, 13, 14, 15] # triple events
|
||||
|
||||
def calc(self, evtdata, hkdata):
|
||||
time = evtdata['time']
|
||||
tstart = Mission(time[0]).to_datetime()
|
||||
escale = self._caldb_energy.scale()
|
||||
ebot = self._caldb_energy.bot()
|
||||
etop = self._caldb_energy.top()
|
||||
|
||||
tempdata = hkdata['td1']
|
||||
temperature = interp1d(
|
||||
hkdata['time'], tempdata, bounds_error=False, kind='linear',
|
||||
fill_value=(tempdata[0], tempdata[-1]))(evtdata['time'])
|
||||
|
||||
if self._seed is not None:
|
||||
sseq = np.random.SeedSequence(self._seed)
|
||||
else:
|
||||
sseq = np.random.SeedSequence()
|
||||
|
||||
rng = np.random.default_rng(sseq)
|
||||
seed = sseq.entropy
|
||||
|
||||
energybot, sigmabot, maskbot \
|
||||
= self._calc(
|
||||
evtdata,
|
||||
('raw_x' ,
|
||||
'pha_bot_sub1',
|
||||
'pha_bot' ,
|
||||
'pha_bot_add1'),
|
||||
temperature, ebot, rng)
|
||||
energytop, sigmatop, masktop \
|
||||
= self._calc(
|
||||
evtdata,
|
||||
('raw_y' ,
|
||||
'pha_top_sub1',
|
||||
'pha_top' ,
|
||||
'pha_top_add1'),
|
||||
temperature, etop, rng)
|
||||
|
||||
energy = np.zeros(evtdata.size, np.double)
|
||||
mask = np.zeros((8, evtdata.size), bool)
|
||||
|
||||
mask[2:5,:] = masktop
|
||||
mask[5:8,:] = maskbot
|
||||
|
||||
emask = np.any(mask[2:,:], axis=0)
|
||||
|
||||
energybot = np.sum(energybot * maskbot , axis=0)
|
||||
sigmabot2 = np.sum(np.power(sigmabot, 2.) * maskbot, axis=0)
|
||||
|
||||
energytop = np.sum(energytop * masktop , axis=0)
|
||||
sigmatop2 = np.sum(np.power(sigmatop, 2.) * masktop, axis=0)
|
||||
|
||||
energy[emask] \
|
||||
= ((energybot * sigmatop2 + energytop * sigmabot2) / (sigmabot2 + sigmatop2))[emask]
|
||||
energy[emask] = self._scale(evtdata['time'][emask], energy[emask], escale)
|
||||
|
||||
# energy = self._scale(energy, escale)
|
||||
pi = self._en2pi(energy, emask )
|
||||
grade = self._grade(mask )
|
||||
|
||||
return energy, pi, grade, temperature, energybot, energytop, seed
|
||||
|
||||
def _calc(self, evtdata, coln, temperature, energycal, rng):
|
||||
strip, mask \
|
||||
= self._eventindex(evtdata[coln[0]])
|
||||
pha = np.array([evtdata[coln[1]], evtdata[coln[2]], evtdata[coln[3]]])
|
||||
|
||||
e1 = self._pha2en(pha , strip, temperature, energycal)
|
||||
e2 = self._pha2en(pha + 1., strip, temperature, energycal)
|
||||
|
||||
# energy = np.random.uniform(e1, e2)
|
||||
energy = rng.uniform(e1, e2)
|
||||
|
||||
sigma = energycal['fwhm_0'][strip] + energycal['fwhm_1'][strip] * temperature
|
||||
mask = np.logical_and(mask, energy > energycal['threshold'][strip])
|
||||
|
||||
return energy, sigma, mask
|
||||
|
||||
def _pha2en(self, pha, strip, temperature, energycal):
|
||||
''' Energy = c00 + c01*T + (c10 + c11*T)*PHA + (c20 + c21*T)*PHA**2 '''
|
||||
pha2 = np.power(pha, 2.)
|
||||
energy = \
|
||||
energycal["c0_0"][strip] + energycal["c0_1"][strip] * temperature + \
|
||||
(energycal["c1_0"][strip] + energycal["c1_1"][strip] * temperature) * pha + \
|
||||
(energycal["c2_0"][strip] + energycal["c2_1"][strip] * temperature) * pha2
|
||||
|
||||
return energy
|
||||
|
||||
def _scale(self, times, energy, escale):
|
||||
''' Еnergy = C0 + C1*Еnergy + С2*Еnergy**2 '''
|
||||
# return escale['C0'] + escale['C1'] * energy + escale['C2'] * np.power(energy, 2.)
|
||||
|
||||
C0 = escale['C0'](times)
|
||||
C1 = escale['C1'](times)
|
||||
|
||||
return energy * C0 + C1
|
||||
|
||||
|
||||
def _en2pi(self, energy, mask):
|
||||
PHA2PI = 10 # channels are in units of 100 eV
|
||||
|
||||
pi = np.int16(energy * PHA2PI) - 1
|
||||
pi = np.maximum(pi, 0)
|
||||
pi[~mask] = 0
|
||||
|
||||
return pi
|
||||
|
||||
def _eventindex(self, strip):
|
||||
coord = np.empty((3, strip.size), int) #(strip, (3, 1))
|
||||
coord[0, :] = strip - 1
|
||||
coord[1, :] = strip
|
||||
coord[2, :] = strip + 1
|
||||
mask = (coord >= 0) & (coord <= 47)
|
||||
coord[np.logical_not(mask)] = -1
|
||||
return coord, mask
|
||||
|
||||
def _grade(self, mask):
|
||||
grademask = np.packbits(mask, axis=0)[0]
|
||||
return self.GRADES[grademask].astype(int)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
206
artpipeline/src/artpipeline/energy.py
Executable file
206
artpipeline/src/artpipeline/energy.py
Executable file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2020-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
import astropy.units as u
|
||||
from astropy.time.formats import erfa
|
||||
from astropy.time import Time
|
||||
from astropy.time import TimezoneInfo
|
||||
|
||||
# from .time import Mission
|
||||
|
||||
from arttools.telescope import Teldef
|
||||
|
||||
np.seterr(all="ignore")
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
|
||||
class Energy(object):
|
||||
|
||||
def __init__(self, caldb, seed=None):
|
||||
self._caldb = caldb
|
||||
self._seed = seed
|
||||
|
||||
self.MJDCDB = Time(58677.6464931, format='mjd')
|
||||
self.MJDREF = Time(51543.8750000, format='mjd') #corresponds to date 01.01.2000 00:00:00 (UTC+3)
|
||||
self.TZ_UTC = TimezoneInfo(utc_offset=0*u.hour) #UTC time zone
|
||||
self.TZ_MSK = TimezoneInfo(utc_offset=3*u.hour) #UTC+3 (Moscow) time zone
|
||||
|
||||
self.GRADES = np.ones(256) * (-1)
|
||||
self.GRADES[18] = 0 # single central events
|
||||
self.GRADES[[50, 26, 22, 19, 54, 51, 30, 27]] = [1, 2, 3, 4, 5, 6, 7, 8] # double events
|
||||
self.GRADES[[58, 62, 59, 23, 55, 31, 63]] = [9, 10, 11, 12, 13, 14, 15] # triple events
|
||||
|
||||
def _calc_mjdtime(self, missiontime):
|
||||
return self.MJDREF + Time(missiontime / erfa.DAYSEC, format='mjd').mjd
|
||||
|
||||
def _calc_dt(self, missiontime):
|
||||
mjdt = self._calc_mjdtime(missiontime)
|
||||
return (mjdt - self.MJDCDB).jd
|
||||
|
||||
def _eventindex(self, strip):
|
||||
# coord = np.empty((3, strip.size), int) #(strip, (3, 1))
|
||||
coord = np.empty((3, strip.count()), int) #(strip, (3, 1))
|
||||
coord[0, :] = strip - 1
|
||||
coord[1, :] = strip
|
||||
coord[2, :] = strip + 1
|
||||
mask = (coord >= 0) & (coord <= 47)
|
||||
coord[np.logical_not(mask)] = -1
|
||||
return coord, mask
|
||||
|
||||
def _calc(self, evtdata, coln, dt, dt2, random, energycal):
|
||||
data_size = dt.size
|
||||
strip, mask_strip \
|
||||
= self._eventindex(evtdata.column(coln[0]))
|
||||
pha0 = np.array([
|
||||
evtdata.column(coln[1]),
|
||||
evtdata.column(coln[2]),
|
||||
evtdata.column(coln[3])
|
||||
], dtype=np.uint32)
|
||||
|
||||
C0 = energycal['C0_0'][strip] + energycal['C0_1'][strip] * dt + energycal['C0_2'][strip] * dt2
|
||||
C1 = energycal['C1_0'][strip] + energycal['C1_1'][strip] * dt + energycal['C1_2'][strip] * dt2
|
||||
C2 = energycal['C2_0'][strip] + energycal['C2_1'][strip] * dt + energycal['C2_2'][strip] * dt2
|
||||
C3 = energycal['C3_0'][strip] + energycal['C3_1'][strip] * dt + energycal['C3_2'][strip] * dt2
|
||||
C4 = energycal['C4_0'][strip] + energycal['C4_1'][strip] * dt + energycal['C4_2'][strip] * dt2
|
||||
|
||||
if random is not None:
|
||||
pha_rand = random.uniform(0, 1, pha0.shape)
|
||||
else:
|
||||
pha_rand = np.zeros(pha0.shape)
|
||||
|
||||
pha = pha0 + pha_rand
|
||||
|
||||
energy3 = C0 + C1 * pha + C2 * np.power(pha, 2)
|
||||
|
||||
kofA = [0.9, 1.15]
|
||||
dayA = [223., 1480.]
|
||||
|
||||
C1_thr = (kofA[0] - kofA[1]) / (dayA[0] - dayA[1])
|
||||
C0_thr = ((kofA[0] + kofA[1]) - (dayA[0] + dayA[1]) * C1_thr) / 2.
|
||||
|
||||
kof = C0_thr + C1_thr * dt
|
||||
|
||||
mask_threshold = energy3 > energycal['THRESHOLD'][strip] * kof
|
||||
mask_threshold[1] = np.full_like(mask_threshold[1], True)
|
||||
|
||||
mask3 = np.logical_and(mask_strip, mask_threshold)
|
||||
|
||||
energy3[~mask3] = 0
|
||||
energy = np.sum(energy3, axis=0)
|
||||
|
||||
# energy = np.sum(energy3 * mask3, axis=0)
|
||||
|
||||
fwhm = (C3[1] + C4[1] * np.sqrt(energy))
|
||||
|
||||
return energy, fwhm, mask3
|
||||
|
||||
def _grade(self, mask):
|
||||
grademask = np.packbits(mask, axis=0)[0]
|
||||
|
||||
return self.GRADES[grademask].astype(int)
|
||||
|
||||
def calc(self, teln, evtdata, random=None):
|
||||
dt = self._calc_dt(evtdata.column('TIME'))
|
||||
dt2 = np.power(dt, 2)
|
||||
|
||||
energybot, fwhmbot, maskbot3 =\
|
||||
self._calc(
|
||||
evtdata,
|
||||
('RAW_X' ,
|
||||
'PHA_BOT_SUB1',
|
||||
'PHA_BOT' ,
|
||||
'PHA_BOT_ADD1'),
|
||||
dt, dt2, random, self._caldb.bot()
|
||||
)
|
||||
fwhmbot_2 = np.power(fwhmbot, 2.)
|
||||
|
||||
energytop, fwhmtop, masktop3 =\
|
||||
self._calc(
|
||||
evtdata,
|
||||
('RAW_Y' ,
|
||||
'PHA_TOP_SUB1',
|
||||
'PHA_TOP' ,
|
||||
'PHA_TOP_ADD1'),
|
||||
dt, dt2, random, self._caldb.top()
|
||||
)
|
||||
fwhmtop_2 = np.power(fwhmtop, 2.)
|
||||
|
||||
# -- calculate mask
|
||||
mask = np.zeros((8, evtdata.size), bool)
|
||||
mask[2:5,:] = masktop3
|
||||
mask[5:8,:] = maskbot3
|
||||
|
||||
# energy = np.zeros(dt.size, float)
|
||||
energy =\
|
||||
((energybot * fwhmtop_2 + energytop * fwhmbot_2) / (fwhmbot_2 + fwhmtop_2))
|
||||
|
||||
grades = self._grade(mask)
|
||||
|
||||
return energy, energybot, energytop, grades
|
||||
|
||||
|
||||
class Flag(object):
|
||||
|
||||
def __init__(self, caldb):
|
||||
self._caldb = caldb
|
||||
|
||||
def _mask_events(self, evtdata, tstart, tstop):
|
||||
times = evtdata.column('TIME')
|
||||
# try:
|
||||
# tstart = evtdata.tstart
|
||||
# tstop = evtdata.tstop
|
||||
# except KeyError:
|
||||
# tstart = times[ 0]
|
||||
# tstop = times[-1]
|
||||
# print('WARNING: TSTART or TSTOP keywords not found')
|
||||
# print('WARNING: will use event times TSTART = {}, TSTOP = {}'.format(tstart, tstop))
|
||||
|
||||
return (times > tstart) & (times < tstop)
|
||||
|
||||
def _mask_shadow(self, evtdata):
|
||||
evtrawx = evtdata.column('RAW_X')
|
||||
evtrawy = evtdata.column('RAW_Y')
|
||||
|
||||
shadowmask = self._caldb.get()
|
||||
|
||||
return np.logical_not(shadowmask[evtrawx, evtrawy])
|
||||
|
||||
def _mask_edges(self, evtdata):
|
||||
evtrawx = evtdata.column('RAW_X')
|
||||
evtrawy = evtdata.column('RAW_Y')
|
||||
|
||||
return np.any([
|
||||
evtrawx == 0, evtrawx == 47,
|
||||
evtrawy == 0, evtrawy == 47], axis=0)
|
||||
|
||||
def calc(self, evtdata, tstart, tstop):
|
||||
flag = np.ones(evtdata.size, dtype=np.uint8)
|
||||
|
||||
eventsmask = self._mask_events(evtdata, tstart, tstop)
|
||||
shadowmask = self._mask_shadow(evtdata)
|
||||
edgesmask = self._mask_edges (evtdata)
|
||||
|
||||
flag[eventsmask] = 0
|
||||
flag[shadowmask] = 2
|
||||
flag[edgesmask ] = 3
|
||||
|
||||
return flag
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
176
artpipeline/src/artpipeline/events.py
Executable file
176
artpipeline/src/artpipeline/events.py
Executable file
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import numpy as np
|
||||
|
||||
from astropy.io import fits
|
||||
|
||||
from arttools.gti import Gti
|
||||
from arttools.datatable import DataTable, FitsAdapter
|
||||
|
||||
from artpipeline.version import __version__
|
||||
|
||||
|
||||
class Events(object):
|
||||
|
||||
def __init__(self, srcfile):
|
||||
super().__init__()
|
||||
|
||||
self._srcfile = srcfile
|
||||
|
||||
self._telname = 0
|
||||
self._teln = 0
|
||||
|
||||
self._tstart = 0
|
||||
self._tstop = 0
|
||||
|
||||
self._events = DataTable()
|
||||
self._hk = DataTable()
|
||||
self._gti = Gti()
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self._events.size
|
||||
|
||||
@property
|
||||
def teln(self): return self._teln
|
||||
|
||||
@property
|
||||
def telname(self): return self._telname
|
||||
|
||||
@property
|
||||
def tstart(self): return self._tstart
|
||||
|
||||
@property
|
||||
def tstop(self): return self._tstop
|
||||
|
||||
@property
|
||||
def events(self): return self._events
|
||||
|
||||
@property
|
||||
def hk(self): return self._hk
|
||||
|
||||
@property
|
||||
def gti(self): return self._gti
|
||||
|
||||
def _read_header(self):
|
||||
with fits.open(self._srcfile) as hdul:
|
||||
self._teln = hdul['events'].header['teln' ]
|
||||
self._telname = hdul['events'].header['instrume']
|
||||
self._tstart = hdul['events'].header['tstart' ]
|
||||
self._tstop = hdul['events'].header['tstop' ]
|
||||
|
||||
def read(self):
|
||||
self._read_header()
|
||||
|
||||
self._events = DataTable().adapter(FitsAdapter(self._srcfile, 'events')).read()
|
||||
self._hk = DataTable().adapter(FitsAdapter(self._srcfile, 'hk' )).read()
|
||||
self._gti = Gti.from_hduname(self._srcfile, 'stdgti')
|
||||
|
||||
return self
|
||||
|
||||
def write_cl(self, dstfile, cdbinfo):
|
||||
|
||||
srchdul = fits.open(self._srcfile)
|
||||
|
||||
# --- primary HDU ---
|
||||
hdu = fits.PrimaryHDU()
|
||||
|
||||
# --- EVENTS HDU ---
|
||||
a00 = np.array(self._events.column('TIME' ))
|
||||
a01 = np.array(self._events.column('TIME_I' ))
|
||||
a02 = np.array(self._events.column('TIME_F' ))
|
||||
a03 = np.array(self._events.column('TIME_CORR' ))
|
||||
a04 = np.array(self._events.column('ENERGY' ))
|
||||
a05 = np.array(self._events.column('ENERGY_TOP' ))
|
||||
a06 = np.array(self._events.column('ENERGY_BOT' ))
|
||||
a07 = np.array(self._events.column('GRADE' ))
|
||||
a08 = np.array(self._events.column('FLAG' ))
|
||||
a09 = np.array(self._events.column('RA' ))
|
||||
a10 = np.array(self._events.column('DEC' ))
|
||||
a11 = np.array(self._events.column('RAW_X' ))
|
||||
a12 = np.array(self._events.column('RAW_Y' ))
|
||||
a13 = np.array(self._events.column('PHA_BOT' ))
|
||||
a14 = np.array(self._events.column('PHA_BOT_ADD1'))
|
||||
a15 = np.array(self._events.column('PHA_BOT_SUB1'))
|
||||
a16 = np.array(self._events.column('PHA_TOP' ))
|
||||
a17 = np.array(self._events.column('PHA_TOP_ADD1'))
|
||||
a18 = np.array(self._events.column('PHA_TOP_SUB1'))
|
||||
a19 = np.array(self._events.column('TRIGGER' ))
|
||||
|
||||
c00 = fits.Column(name='TIME' , format='1D', unit='sec', array=a00 )
|
||||
c01 = fits.Column(name='TIME_I' , format='1J', unit='sec', array=a01, bzero=2147483648)
|
||||
c02 = fits.Column(name='TIME_F' , format='1D', unit='sec', array=a02 )
|
||||
c03 = fits.Column(name='TIME_CORR' , format='1D', unit='sec', array=a03 )
|
||||
c04 = fits.Column(name='ENERGY' , format='1D', unit='keV', array=a04 )
|
||||
c05 = fits.Column(name='ENERGY_TOP' , format='1D', unit='keV', array=a05 )
|
||||
c06 = fits.Column(name='ENERGY_BOT' , format='1D', unit='keV', array=a06 )
|
||||
c07 = fits.Column(name='GRADE' , format='1I', unit='' , array=a07 )
|
||||
c08 = fits.Column(name='FLAG' , format='1I', unit='' , array=a08 )
|
||||
c09 = fits.Column(name='RA' , format='1D', unit='deg', array=a09 )
|
||||
c10 = fits.Column(name='DEC' , format='1D', unit='deg', array=a10 )
|
||||
c11 = fits.Column(name='RAW_X' , format='1I', unit='pix', array=a11 )
|
||||
c12 = fits.Column(name='RAW_Y' , format='1I', unit='pix', array=a12 )
|
||||
c13 = fits.Column(name='PHA_BOT' , format='1I', unit='' , array=a13 )
|
||||
c14 = fits.Column(name='PHA_BOT_ADD1', format='1I', unit='' , array=a14 )
|
||||
c15 = fits.Column(name='PHA_BOT_SUB1', format='1I', unit='' , array=a15 )
|
||||
c16 = fits.Column(name='PHA_TOP' , format='1I', unit='' , array=a16 )
|
||||
c17 = fits.Column(name='PHA_TOP_ADD1', format='1I', unit='' , array=a17 )
|
||||
c18 = fits.Column(name='PHA_TOP_SUB1', format='1I', unit='' , array=a18 )
|
||||
c19 = fits.Column(name='TRIGGER' , format='1I', unit='' , array=a19 )
|
||||
|
||||
cols = fits.ColDefs([
|
||||
c00, c01, c02, c03, c04, c05, c06, c07, c08, c09,
|
||||
c10, c11, c12, c13, c14, c15, c16, c17, c18, c19
|
||||
])
|
||||
|
||||
evthdu = fits.BinTableHDU.from_columns(cols)
|
||||
evthdu.name = 'EVENTS'
|
||||
|
||||
creator = 'artpipeline v.{}'.format(__version__)
|
||||
|
||||
evthdu.header['creator' ] = (creator , 'program and version that created this file' )
|
||||
evthdu.header['origin' ] = ('IKI RAS' , 'institution that created this file' )
|
||||
evthdu.header['telescop'] = ('SRG/ART-XC', 'mission name' )
|
||||
|
||||
# copy header keys
|
||||
COPYKEYS = [
|
||||
'instrume', 'teln', 'detn', 'urdn',
|
||||
'mjdref', 'artday', 'tstart', 'tstop',
|
||||
'date', 'date-obs', 'time-obs', 'date-end', 'time-end',
|
||||
'maxticks', 'timedel', 'timeunit', 'timeref', 'tassign'
|
||||
]
|
||||
|
||||
for key in COPYKEYS:
|
||||
try:
|
||||
evthdu.header[key] = srchdul['EVENTS'].header[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
# add caldb key
|
||||
for key in cdbinfo:
|
||||
evthdu.header[key] = cdbinfo[key]
|
||||
|
||||
# --- make file ---
|
||||
# hdulist = fits.HDUList([hdu, evthdu, srchdul['']])
|
||||
hdulist = fits.HDUList([hdu, evthdu, srchdul['KVEA'], srchdul['HK'], srchdul['STDGTI']])
|
||||
|
||||
hdulist.writeto(dstfile, overwrite=True, checksum=True)
|
||||
|
||||
|
||||
def setcol(self, name, arr):
|
||||
self._events.setcol(name, arr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
302
artpipeline/src/artpipeline/orientation.py
Normal file
302
artpipeline/src/artpipeline/orientation.py
Normal file
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2020-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
import numpy as np
|
||||
from scipy.spatial.transform import Slerp
|
||||
from scipy.spatial.transform import Rotation
|
||||
|
||||
from astropy.io import fits
|
||||
from astropy.nddata import bitmask
|
||||
|
||||
from arttools.telescope import Teldef
|
||||
|
||||
|
||||
class AttitudeStatus(object):
|
||||
|
||||
INTERP = 1
|
||||
SCIREADY = 2
|
||||
PRECSTAB = 4
|
||||
SED1_ON = 8
|
||||
SED2_ON = 16
|
||||
BOKZ_ON = 32
|
||||
SED1_MEAS = 64
|
||||
SED2_MEAS = 128
|
||||
BOKZ_MEAS = 256
|
||||
|
||||
flags = np.array([
|
||||
INTERP ,
|
||||
SCIREADY ,
|
||||
PRECSTAB ,
|
||||
SED1_ON ,
|
||||
SED2_ON ,
|
||||
BOKZ_ON ,
|
||||
SED1_MEAS,
|
||||
SED2_MEAS,
|
||||
BOKZ_MEAS
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def flags_except(cls, flags):
|
||||
indices = np.searchsorted(cls.flags, flags)
|
||||
return np.delete(cls.flags, indices)
|
||||
|
||||
@classmethod
|
||||
def pack(cls, arr):
|
||||
bits = [cls.flags[x.astype(bool)] for x in arr.T]
|
||||
return [bitmask.interpret_bit_flags(x) for x in bits]
|
||||
|
||||
@classmethod
|
||||
def flagval(cls, val, flags):
|
||||
return bitmask.bitfield_to_boolean_mask(val, ignore_flags=flags)
|
||||
|
||||
@classmethod
|
||||
def extract(cls, arr, flags):
|
||||
return np.array([AttitudeStatus.flagval(x, flags) for x in arr])
|
||||
|
||||
@staticmethod
|
||||
def tostr(val, fmt='{0:016b}'):
|
||||
return fmt.format(val)
|
||||
|
||||
|
||||
class Orientation(object):
|
||||
|
||||
UNITY_QUAT = np.array([0., 0., 0., 1.])
|
||||
|
||||
VSC = {
|
||||
'X': np.array([1., 0., 0.]),
|
||||
'Y': np.array([0., 1., 0.]),
|
||||
'Z': np.array([0., 0., 1.])}
|
||||
|
||||
OPTICAL_AXIS = VSC['X']
|
||||
NORTH = VSC['Z']
|
||||
|
||||
RELCORR_GTI = [
|
||||
[624390347, 624399808],
|
||||
[624410643, 630954575]]
|
||||
|
||||
def __init__(self, caldb):
|
||||
self._caldb = caldb
|
||||
|
||||
def calc_events_coords(self, oridata, evtdata, telname, random=None):
|
||||
oritime = np.array(oridata['time'])
|
||||
evttime = np.array(evtdata.column('TIME' ))
|
||||
rawx0 = np.array(evtdata.column('RAW_X'))
|
||||
rawy0 = np.array(evtdata.column('RAW_Y'))
|
||||
|
||||
mask = np.logical_and(evttime > oritime[0], evttime < oritime[-1])
|
||||
|
||||
if random is not None:
|
||||
rawx_rand = random.uniform(-0.5, 0.5, rawx0.shape)
|
||||
rawy_rand = random.uniform(-0.5, 0.5, rawy0.shape)
|
||||
else:
|
||||
rawx_rand = 0
|
||||
rawy_rand = 0
|
||||
|
||||
rawx = rawx0 + rawx_rand
|
||||
rawy = rawy0 + rawy_rand
|
||||
|
||||
orient = Slerp(oridata['time'], oridata['quat'])
|
||||
telquat = Rotation(self._caldb.read_boresight(telname))
|
||||
|
||||
evtquat = np.zeros_like(evttime)
|
||||
ra = np.zeros_like(evttime)
|
||||
dec = np.zeros_like(evttime)
|
||||
|
||||
evtquat = orient(evttime[mask]) * telquat
|
||||
|
||||
ra, dec = self.rawxy_to_radec(evtquat, rawx[mask], rawy[mask])
|
||||
|
||||
ra_deg = np.zeros_like(evttime)
|
||||
dec_deg = np.zeros_like(evttime)
|
||||
|
||||
ra_deg[mask] = np.rad2deg(ra)
|
||||
dec_deg[mask] = np.rad2deg(dec)
|
||||
|
||||
return ra_deg, dec_deg, mask
|
||||
|
||||
def calc_attitudes(self, oridata, instname=None):
|
||||
instquat = self.instquat(instname)
|
||||
quatdata = oridata['quat_sc']
|
||||
if instname != None:
|
||||
quatdata = oridata['quat']
|
||||
|
||||
orient = Slerp (oridata['time'], quatdata)
|
||||
attquat = orient(oridata['time']) * Rotation(instquat)
|
||||
|
||||
ra, dec, roll \
|
||||
= self.calc_radecroll(attquat)
|
||||
|
||||
return np.rad2deg(ra), np.rad2deg(dec), np.rad2deg(roll)
|
||||
|
||||
def calc_rawxy(self, oritime, oriquat, evttime, ra, dec, telname):
|
||||
ra = np.deg2rad(ra )
|
||||
dec = np.deg2rad(dec)
|
||||
|
||||
orient = Slerp(oritime, oriquat)
|
||||
telquat = Rotation(self._caldb.read_boresight(telname))
|
||||
evtquat = orient(evttime) * telquat
|
||||
|
||||
rawx, rawy \
|
||||
= self.radec_to_rawxy(evtquat, ra, dec)
|
||||
|
||||
return rawx, rawy
|
||||
|
||||
def calc_radec(self, oritime, oriquat, evttime, evtrawx, evtrawy, telname):
|
||||
orient = Slerp(oritime, oriquat)
|
||||
telquat = Rotation(self._caldb.read_boresight(telname))
|
||||
|
||||
evtquat = orient(evttime) * telquat
|
||||
|
||||
ra, dec = self.rawxy2radec(evtquat, evtrawx, evtrawy)
|
||||
|
||||
return np.rad2deg(ra), np.rad2deg(dec)
|
||||
|
||||
def calc_radecroll(self, quat):
|
||||
oaxis = self.vec_normalize(quat.apply(self.OPTICAL_AXIS))
|
||||
|
||||
ra, dec \
|
||||
= self.vec_to_pol(oaxis)
|
||||
|
||||
YZ = self.vec_normalize(np.cross(oaxis, self.NORTH))
|
||||
roll = np.arctan2(
|
||||
np.sum(YZ * quat.apply(self.VSC['Z']), axis=1),
|
||||
np.sum(YZ * quat.apply(self.VSC['Y']), axis=1))
|
||||
|
||||
return ra, dec, roll
|
||||
|
||||
def instquat(self, instname=None):
|
||||
if instname == None:
|
||||
return self.UNITY_QUAT
|
||||
|
||||
if instname != 'GYRO':
|
||||
return self._caldb.read_boresight(instname)
|
||||
|
||||
return self.UNITY_QUAT
|
||||
|
||||
def read(self, orifile):
|
||||
times, quats, state = self._read_gyro(orifile)
|
||||
|
||||
quat = Rotation(quats) * Rotation(self._caldb.instquat()) * Rotation(self._caldb.boresight())
|
||||
quat_sc = Rotation(quats) * Rotation(self._caldb.instquat())
|
||||
|
||||
return {
|
||||
'size' : times.size,
|
||||
'time' : times ,
|
||||
'quat' : quat ,
|
||||
'quat_sc': quat_sc ,
|
||||
'state' : state }
|
||||
|
||||
def _read_gyro(self, orifile):
|
||||
ori_hdul = fits.open(orifile)
|
||||
data_size = ori_hdul[1].data.size
|
||||
times = ori_hdul[1].data['TIME' ]
|
||||
q0 = ori_hdul[1].data['QORT_1']
|
||||
q1 = ori_hdul[1].data['QORT_2']
|
||||
q2 = ori_hdul[1].data['QORT_3']
|
||||
q3 = ori_hdul[1].data['QORT_0']
|
||||
|
||||
times -= self._caldb.timeshift()
|
||||
quats = np.array([q0, q1, q2, q3]).T
|
||||
|
||||
state = np.array([
|
||||
np.zeros(data_size, dtype=int),
|
||||
ori_hdul[1].data['ST_SCIREADY' ],
|
||||
ori_hdul[1].data['ST_PRECSTAB' ],
|
||||
ori_hdul[1].data['ST_SED1_ON' ],
|
||||
ori_hdul[1].data['ST_SED2_ON' ],
|
||||
ori_hdul[1].data['ST_BOKZ_ON' ],
|
||||
ori_hdul[1].data['ST_SED1_MEAS' ],
|
||||
ori_hdul[1].data['ST_SED2_MEAS' ],
|
||||
ori_hdul[1].data['ST_BOKZ_MEAS' ]
|
||||
])
|
||||
|
||||
return times, quats, state
|
||||
|
||||
def vec_normalize(self, vec):
|
||||
norm = np.sqrt(np.sum(vec**2, axis=-1))[..., np.newaxis]
|
||||
|
||||
return vec / norm
|
||||
|
||||
def rawxy_to_offset(self, rawx, rawy):
|
||||
xoffset = (rawx - Teldef.centeroffset) * Teldef.d
|
||||
yoffset = (rawy - Teldef.centeroffset) * Teldef.d
|
||||
|
||||
return xoffset, yoffset
|
||||
|
||||
def offset_to_rawxy(self, xoffset, yoffset):
|
||||
x = xoffset / Teldef.d + Teldef.centeroffset + 0.5
|
||||
y = yoffset / Teldef.d + Teldef.centeroffset + 0.5
|
||||
|
||||
# return x.astype(np.int) - (x < 0), y.astype(np.int) - (y < 0)
|
||||
# return x.astype(np.int), y.astype(np.int)
|
||||
return x.astype(int), y.astype(int)
|
||||
|
||||
def offset_to_vec(self, x, y):
|
||||
vec = np.empty(shape=x.shape+(3,), dtype=np.double)
|
||||
vec[..., 0] = Teldef.F
|
||||
vec[..., 1] = x
|
||||
vec[..., 2] = -y
|
||||
|
||||
return vec
|
||||
|
||||
def vec_to_offset(self, vec):
|
||||
x = vec[..., 0]
|
||||
y = vec[..., 1]
|
||||
z = vec[..., 2]
|
||||
|
||||
xoffset = y * Teldef.F / x
|
||||
yoffset = -z * Teldef.F / x
|
||||
|
||||
return xoffset, yoffset
|
||||
|
||||
def vec_to_pol(self, vec):
|
||||
x = vec[..., 0]
|
||||
y = vec[..., 1]
|
||||
z = vec[..., 2]
|
||||
h = np.hypot(x, y)
|
||||
|
||||
phi = np.arctan2(y, x) % (2. * np.pi)
|
||||
theta = np.arctan2(z, h)
|
||||
|
||||
return phi, theta
|
||||
|
||||
def pol_to_vec(self, phi, theta):
|
||||
vec = np.empty(shape=phi.shape+(3,), dtype=np.double)
|
||||
|
||||
vec[..., 0] = np.cos(theta) * np.cos(phi)
|
||||
vec[..., 1] = np.cos(theta) * np.sin(phi)
|
||||
vec[..., 2] = np.sin(theta)
|
||||
|
||||
return vec
|
||||
|
||||
def rawxy_to_radec(self, quat, rawx, rawy):
|
||||
xoffset, yoffset \
|
||||
= self.rawxy_to_offset(rawx, rawy)
|
||||
xyvec = self.offset_to_vec(xoffset, yoffset)
|
||||
ra, dec = self.vec_to_pol(quat.apply(xyvec))
|
||||
|
||||
return ra, dec
|
||||
|
||||
def radec_to_rawxy(self, quat, ra, dec):
|
||||
skyvec = self.pol_to_vec(ra, dec)
|
||||
detvec = quat.apply(skyvec, inverse=True)
|
||||
xoffset, yoffset \
|
||||
= self.vec_to_offset(detvec)
|
||||
rawx, rawy \
|
||||
= self.offset_to_rawxy(xoffset, yoffset)
|
||||
|
||||
return rawx, rawy
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
518
artpipeline/src/artpipeline/pipeline.py
Executable file
518
artpipeline/src/artpipeline/pipeline.py
Executable file
@@ -0,0 +1,518 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import argparse
|
||||
|
||||
import datetime
|
||||
|
||||
import astropy.units as u
|
||||
from astropy.time.formats import erfa
|
||||
from astropy.time import Time
|
||||
from astropy.time import TimezoneInfo
|
||||
|
||||
TZ_UTC = TimezoneInfo(utc_offset=0*u.hour) # UTC time zone
|
||||
TZ_MSK = TimezoneInfo(utc_offset=3*u.hour) # UTC+3 (Moscow) time zone
|
||||
|
||||
import numpy as np
|
||||
|
||||
from arttools.cmd import Command, ExecutionResult
|
||||
from arttools.task import (
|
||||
Task, TaskError, TaskArgs, TaskCmds
|
||||
)
|
||||
from arttools.errors import Error
|
||||
from arttools.fstools import Dir
|
||||
from arttools.argparse import KvAction
|
||||
from arttools.telescope import Teldef
|
||||
from arttools.random import Random
|
||||
from arttools.arttime import ArtDay, MissionTime
|
||||
|
||||
from artpipeline.version import __version__
|
||||
|
||||
from artpipeline.caldb import CaldbInfo
|
||||
from artpipeline.caldb import CaldbMask
|
||||
from artpipeline.caldb import CaldbEnergy
|
||||
from artpipeline.caldb import CaldbOrientation
|
||||
|
||||
from artpipeline.events import Events
|
||||
from artpipeline.energy import Flag
|
||||
from artpipeline.energy import Energy
|
||||
from artpipeline.skyaxis import SkyAxis
|
||||
from artpipeline.skycoord import SkyCoord
|
||||
|
||||
|
||||
class Args(TaskArgs):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def read_env(self):
|
||||
if not 'ARTCALDB' in os.environ:
|
||||
raise TaskError('ARTCALDB not found (environment variable not set?)')
|
||||
|
||||
self.caldbdir = os.environ['ARTCALDB']
|
||||
|
||||
def read_cfg(self, cfgfile):
|
||||
pass
|
||||
|
||||
def read_args(self):
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument(
|
||||
'--stem',
|
||||
help='newfile creation stem',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--stem-in',
|
||||
type=str,
|
||||
help='input files stem',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'srcdir',
|
||||
help='input directory',
|
||||
action=KvAction
|
||||
)
|
||||
argparser.add_argument(
|
||||
'tmpdir',
|
||||
help='temporary directory',
|
||||
action=KvAction
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--dstdir',
|
||||
help='output directory',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
# argparser.add_argument(
|
||||
# '--user-gti',
|
||||
# type=str,
|
||||
# help='user gti file',
|
||||
# action=KvAction,
|
||||
# default=None
|
||||
# )
|
||||
argparser.add_argument(
|
||||
'--seed',
|
||||
type=str,
|
||||
help='random generator seed',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--no-random',
|
||||
help='turn randomization off',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--plain-energy',
|
||||
help='calculate energy on a plain list of files',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--L0',
|
||||
help='process L0 files',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
args = argparser.parse_args()
|
||||
|
||||
self.stem = args.stem
|
||||
self.stem_in = args.stem_in
|
||||
self.srcdir = args.srcdir
|
||||
self.dstdir = args.dstdir
|
||||
self.tmpdir = os.path.join(args.tmpdir, str(self.pid))
|
||||
self.tmpdst = os.path.join(self.tmpdir, 'dst')
|
||||
# self.usergti = args.usergti
|
||||
self.seed = args.seed
|
||||
self.no_random = args.no_random
|
||||
self.plain_energy = args.plain_energy
|
||||
self.process_l0 = args.L0
|
||||
|
||||
if self.dstdir is None:
|
||||
self.dstdir = self.srcdir
|
||||
|
||||
# if self.seed is not None:
|
||||
# self.seed = int(self.seed)
|
||||
|
||||
def print(self):
|
||||
print('Input parameters:')
|
||||
print(' stem = {}'.format(self.stem ))
|
||||
print(' stem-in = {}'.format(self.stem_in ))
|
||||
print(' srcdir = {}'.format(self.srcdir ))
|
||||
print(' dstdir = {}'.format(self.dstdir ))
|
||||
print(' tmpdir = {}'.format(self.tmpdir ))
|
||||
# print(' user-gti = {}'.format(self.usergti ))
|
||||
print(' seed = {}'.format(self.seed ))
|
||||
print(' no random = {}'.format(self.no_random ))
|
||||
print(' plain energy = {}'.format(self.plain_energy))
|
||||
print(' peocess L0 = {}'.format(self.process_l0 ))
|
||||
print()
|
||||
|
||||
caldbinfo = CaldbInfo(self.caldbdir).get()
|
||||
|
||||
print('CALDB version {} [{}]'.format(caldbinfo['CALDBVER'], caldbinfo['CALDBDIR']))
|
||||
print()
|
||||
|
||||
def check(self):
|
||||
if not Dir(self.srcdir).exists():
|
||||
raise TaskError('error: srcdir not found')
|
||||
Dir(self.dstdir).check()
|
||||
Dir(self.tmpdir).check(clean=True)
|
||||
Dir(self.tmpdst).check(clean=True)
|
||||
|
||||
|
||||
class Pipeline(Task):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
print('*** artpipeline v.{}'.format(__version__))
|
||||
|
||||
self._name = 'artpipeline v.{}'.format(__version__)
|
||||
|
||||
self._args = Args()
|
||||
|
||||
self._orifile = None
|
||||
|
||||
def initialize(self):
|
||||
self._args.read()
|
||||
self._args.print()
|
||||
self._args.check()
|
||||
|
||||
if not self._args.no_random:
|
||||
self._random = Random(self._args.seed)
|
||||
else:
|
||||
self._random = None
|
||||
|
||||
def finalize(self):
|
||||
Dir(self._args.tmpdir).delete()
|
||||
|
||||
def runmain(self):
|
||||
layout = self._layout()
|
||||
|
||||
self._make_orient(layout)
|
||||
self._make_attitude(layout)
|
||||
self._process_events(layout)
|
||||
|
||||
print('*** artpipeline finished')
|
||||
|
||||
def _get_stem(self, fname, sep):
|
||||
# get filenme from path
|
||||
fn = os.path.basename(fname)
|
||||
|
||||
#find teln in fname
|
||||
fi = fn.find(sep)
|
||||
|
||||
return fn[:fi]
|
||||
|
||||
def _list_files(self, srcdir, stem_in=None):
|
||||
allfiles = [fi for fi in os.listdir(srcdir)]
|
||||
allfiles.sort()
|
||||
|
||||
if stem_in==None:
|
||||
return allfiles
|
||||
|
||||
return [fi for fi in allfiles if fi.startswith(stem_in)]
|
||||
|
||||
def _plain_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'plain'
|
||||
layout['post'] = ''
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick events
|
||||
srcdir = Dir(self._args.srcdir)
|
||||
eventfiles = self._list_files(srcdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
telfiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
telfiles.append((srcdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = telfiles
|
||||
|
||||
return layout
|
||||
|
||||
def _L0_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'L0'
|
||||
layout['post'] = '_urd.L2'
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick L0 events
|
||||
ufdir = Dir(self._args.srcdir)
|
||||
eventfiles = self._list_files(ufdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
uffiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
uffiles.append((ufdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = uffiles
|
||||
|
||||
# pick orientation
|
||||
orientation = {}
|
||||
|
||||
auxdir = Dir(self._args.srcdir)
|
||||
auxfiles = self._list_files(auxdir.get(), self._args.stem_in)
|
||||
|
||||
for otype in ['gyro', 'sed1', 'sed2', 'bokz']:
|
||||
for auxf in auxfiles:
|
||||
if auxf.endswith('_{}.L0.fits'.format(otype)):
|
||||
ofile = auxdir / auxf
|
||||
|
||||
if ofile.exists():
|
||||
orientation[otype] = ofile.get()
|
||||
|
||||
layout['orientation'] = orientation
|
||||
|
||||
return layout
|
||||
|
||||
def _science_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'science'
|
||||
layout['post'] = '_cl'
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick uf events
|
||||
ufdir = Dir(self._args.srcdir) / 'event_uf'
|
||||
eventfiles = self._list_files(ufdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
uffiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
uffiles.append((ufdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = uffiles
|
||||
|
||||
# pick orientation
|
||||
orientation = {}
|
||||
|
||||
auxdir = Dir(self._args.srcdir) / 'auxiliary'
|
||||
auxfiles = self._list_files(auxdir.get(), self._args.stem_in)
|
||||
|
||||
for otype in ['gyro', 'sed1', 'sed2', 'bokz']:
|
||||
for auxf in auxfiles:
|
||||
if auxf.endswith('_{}.fits'.format(otype)):
|
||||
ofile = auxdir / auxf
|
||||
|
||||
if ofile.exists():
|
||||
orientation[otype] = ofile.get()
|
||||
|
||||
layout['orientation'] = orientation
|
||||
|
||||
return layout
|
||||
|
||||
def _layout(self):
|
||||
if self._args.process_l0:
|
||||
return self._L0_layout()
|
||||
|
||||
if self._args.plain_energy:
|
||||
return self._plain_layout()
|
||||
|
||||
return self._science_layout()
|
||||
|
||||
def _make_orient(self, layout):
|
||||
if layout['type'] == 'plain':
|
||||
print('mode: plain: skipping orienation...')
|
||||
return
|
||||
|
||||
print('making orienation file...')
|
||||
gyrofile = layout['orientation']['gyro']
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(gyrofile, sep='_')
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
if self._args.process_l0:
|
||||
auxdir = Dir(self._args.dstdir)
|
||||
else:
|
||||
auxdir = Dir(self._args.dstdir) / 'auxiliary'
|
||||
|
||||
auxdir.check()
|
||||
|
||||
orifile = '{}_ori.fits'.format(stemout)
|
||||
orifull = (auxdir / orifile).get()
|
||||
|
||||
skyaxis = SkyAxis()
|
||||
skyaxis.process(gyrofile).write(orifull)
|
||||
|
||||
self._orifile = orifull
|
||||
|
||||
print('written: {}'.format(orifull))
|
||||
|
||||
def _make_attitude(self, layout):
|
||||
if layout['type'] == 'plain':
|
||||
print('mode: plain: skipping attitude...')
|
||||
return
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(self._orifile, sep='_')
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
print('making attitude file...')
|
||||
attfile = '{}_att.fits'.format(stemout)
|
||||
|
||||
caldb = CaldbOrientation(self._args.caldbdir, 'GYRO')
|
||||
# skycoord = SkyCoord(caldb, 'GYRO')
|
||||
skycoord = SkyCoord(caldb, self._orifile, 'GYRO')
|
||||
|
||||
# skycoord.attitude(self._orifile).write(attfull)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
skycoord.attitude(TEL).collect()
|
||||
|
||||
if self._args.process_l0:
|
||||
attfull = (Dir(self._args.dstdir) / attfile).get()
|
||||
else:
|
||||
attfull = (Dir(self._args.dstdir) / 'auxiliary' / attfile).get()
|
||||
|
||||
skycoord.write(attfull)
|
||||
|
||||
print('written: {}'.format(attfull))
|
||||
|
||||
def _process_events(self, layout):
|
||||
processed = datetime.datetime.now(tz=TZ_MSK).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
metainfo = {
|
||||
'artpipeline': __version__,
|
||||
'processed' : processed
|
||||
}
|
||||
|
||||
if self._args.plain_energy:
|
||||
metainfo['mode'] = 'plain'
|
||||
elif self._args.process_l0:
|
||||
metainfo['mode'] = 'L0'
|
||||
else:
|
||||
metainfo['mode'] = 'science'
|
||||
|
||||
caldbinfo = CaldbInfo(self._args.caldbdir)
|
||||
|
||||
metainfo['caldb'] = caldbinfo.get()['CALDBVER']
|
||||
|
||||
if not self._args.no_random:
|
||||
random = self._random
|
||||
metainfo['seed'] = random.seed
|
||||
else:
|
||||
random = None
|
||||
metainfo['seed'] = None
|
||||
|
||||
metainfo['events'] = {}
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
print('processing: {}'.format(TEL))
|
||||
|
||||
for fitsname in layout['uf'].get(TEL, []):
|
||||
|
||||
print('event file: {}'.format(fitsname))
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(fitsname, TEL)
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
# read events
|
||||
events = Events(fitsname).read()
|
||||
skyflag = np.zeros_like(events.events.column('TIME'))
|
||||
|
||||
if self._args.plain_energy:
|
||||
times = events.events.column('TIME')
|
||||
events.setcol('RA' , np.zeros_like(times))
|
||||
events.setcol('DEC', np.zeros_like(times))
|
||||
else:
|
||||
# calc ra/dec
|
||||
ocaldb = CaldbOrientation(self._args.caldbdir, 'GYRO')
|
||||
skycoord = SkyCoord(ocaldb, self._orifile)
|
||||
|
||||
skyflag = skycoord.events(self._orifile, events, random)
|
||||
|
||||
# calc energies
|
||||
ecaldb = CaldbEnergy(self._args.caldbdir, TEL)
|
||||
energy = Energy(ecaldb)
|
||||
|
||||
en, enbot, entop, grade =\
|
||||
energy.calc(events.teln, events.events, self._random)
|
||||
|
||||
events.events.setcol('ENERGY' , en )
|
||||
events.events.setcol('ENERGY_BOT', enbot)
|
||||
events.events.setcol('ENERGY_TOP', entop)
|
||||
events.events.setcol('GRADE' , grade)
|
||||
|
||||
# calc flag
|
||||
mcaldb = CaldbMask(self._args.caldbdir, TEL)
|
||||
flag = Flag(mcaldb)
|
||||
evtflag = flag.calc(events.events, events.tstart, events.tstop)
|
||||
|
||||
events.events.setcol('FLAG', skyflag + evtflag)
|
||||
|
||||
# print(events.events.frame)
|
||||
|
||||
if self._args.plain_energy:
|
||||
stemout = self._get_stem(fitsname, '.fits')
|
||||
dstfile = '{}.L2.fits'.format(stemout)
|
||||
else:
|
||||
dstfile = '{}{}{}.fits'.format(stemout, events.telname, layout['post'])
|
||||
|
||||
if self._args.plain_energy or self._args.process_l0:
|
||||
dstdir = Dir(self._args.dstdir)
|
||||
else:
|
||||
dstdir = Dir(self._args.dstdir) / 'event_cl'
|
||||
|
||||
dstdir.check()
|
||||
|
||||
dstfull = (dstdir / dstfile).get()
|
||||
events.write_cl(dstfull, caldbinfo.get())
|
||||
|
||||
aday_start = MissionTime(events.tstart).to_artday()
|
||||
aday_stop = MissionTime(events.tstop ).to_artday()
|
||||
|
||||
metainfo['events'][dstfile] = {
|
||||
'artday' : (aday_start, aday_stop),
|
||||
'mission': (events.tstart, events.tstop),
|
||||
'events' : events.size
|
||||
}
|
||||
|
||||
print('written: {}'.format(dstfull))
|
||||
|
||||
# write meta
|
||||
dstdir = Dir(self._args.dstdir)
|
||||
metafname = dstdir.pathfor('meta.info')
|
||||
with open(metafname, 'w') as metafile:
|
||||
json.dump(metainfo, metafile, indent=4)
|
||||
|
||||
print('written: {}'.format(metafname))
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
Pipeline().run()
|
||||
except TaskError as e:
|
||||
print('error: task execution:', e)
|
||||
except Error as e:
|
||||
print('error: unknown:', e)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
# PipelineTask().runall()
|
||||
573
artpipeline/src/artpipeline/pipeline.py.new.py
Executable file
573
artpipeline/src/artpipeline/pipeline.py.new.py
Executable file
@@ -0,0 +1,573 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import argparse
|
||||
|
||||
import datetime
|
||||
|
||||
import astropy.units as u
|
||||
from astropy.time.formats import erfa
|
||||
from astropy.time import Time
|
||||
from astropy.time import TimezoneInfo
|
||||
|
||||
TZ_UTC = TimezoneInfo(utc_offset=0*u.hour) # UTC time zone
|
||||
TZ_MSK = TimezoneInfo(utc_offset=3*u.hour) # UTC+3 (Moscow) time zone
|
||||
|
||||
import numpy as np
|
||||
|
||||
from arttools.cmd import Command, ExecutionResult
|
||||
from arttools.task import (
|
||||
Task, TaskError, TaskArgs, TaskCmds
|
||||
)
|
||||
from arttools.errors import Error
|
||||
from arttools.fstools import Dir
|
||||
from arttools.argparse import KvAction
|
||||
from arttools.telescope import Teldef
|
||||
from arttools.random import Random
|
||||
from artdactools.arttime import ArtDay, MissionTime
|
||||
|
||||
from artpipeline.version import __version__
|
||||
|
||||
from artpipeline.caldb import CaldbInfo
|
||||
from artpipeline.caldb import CaldbMask
|
||||
from artpipeline.caldb import CaldbEnergy
|
||||
from artpipeline.caldb import CaldbOrientation
|
||||
|
||||
from artpipeline.events import Events
|
||||
from artpipeline.energy import Flag
|
||||
from artpipeline.energy import Energy
|
||||
from artpipeline.skyaxis import SkyAxis
|
||||
from artpipeline.skycoord import SkyCoord
|
||||
|
||||
|
||||
class Args(TaskArgs):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def read_env(self):
|
||||
if not 'ARTCALDB' in os.environ:
|
||||
raise TaskError('ARTCALDB not found (environment variable not set?)')
|
||||
|
||||
self.caldbdir = os.environ['ARTCALDB']
|
||||
|
||||
def read_cfg(self, cfgfile):
|
||||
pass
|
||||
|
||||
def read_args(self):
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument(
|
||||
'--stem',
|
||||
help='newfile creation stem',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--stem-in',
|
||||
type=str,
|
||||
help='input files stem',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'srcdir',
|
||||
help='input directory',
|
||||
action=KvAction
|
||||
)
|
||||
argparser.add_argument(
|
||||
'tmpdir',
|
||||
help='temporary directory',
|
||||
action=KvAction
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--dstdir',
|
||||
help='output directory',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
# argparser.add_argument(
|
||||
# '--user-gti',
|
||||
# type=str,
|
||||
# help='user gti file',
|
||||
# action=KvAction,
|
||||
# default=None
|
||||
# )
|
||||
argparser.add_argument(
|
||||
'--seed',
|
||||
type=str,
|
||||
help='random generator seed',
|
||||
action=KvAction,
|
||||
default=None
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--no-random',
|
||||
help='turn randomization off',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--plain-energy',
|
||||
help='calculate energy on a plain list of files',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
argparser.add_argument(
|
||||
'--L0',
|
||||
help='process L0 files',
|
||||
action='store_true',
|
||||
default=False
|
||||
)
|
||||
args = argparser.parse_args()
|
||||
|
||||
self.stem = args.stem
|
||||
self.stem_in = args.stem_in
|
||||
self.srcdir = args.srcdir
|
||||
self.dstdir = args.dstdir
|
||||
self.tmpdir = os.path.join(args.tmpdir, str(self.pid))
|
||||
self.tmpdst = os.path.join(self.tmpdir, 'dst')
|
||||
# self.usergti = args.usergti
|
||||
self.seed = args.seed
|
||||
self.no_random = args.no_random
|
||||
self.plain_energy = args.plain_energy
|
||||
self.process_l0 = args.L0
|
||||
|
||||
if self.dstdir is None:
|
||||
self.dstdir = self.srcdir
|
||||
|
||||
# if self.seed is not None:
|
||||
# self.seed = int(self.seed)
|
||||
|
||||
def print(self):
|
||||
print('Input parameters:')
|
||||
print(' stem = {}'.format(self.stem ))
|
||||
print(' stem-in = {}'.format(self.stem_in ))
|
||||
print(' srcdir = {}'.format(self.srcdir ))
|
||||
print(' dstdir = {}'.format(self.dstdir ))
|
||||
print(' tmpdir = {}'.format(self.tmpdir ))
|
||||
# print(' user-gti = {}'.format(self.usergti ))
|
||||
print(' seed = {}'.format(self.seed ))
|
||||
print(' no random = {}'.format(self.no_random ))
|
||||
print(' plain energy = {}'.format(self.plain_energy))
|
||||
print(' peocess L0 = {}'.format(self.process_l0 ))
|
||||
print()
|
||||
|
||||
caldbinfo = CaldbInfo(self.caldbdir).get()
|
||||
|
||||
print('CALDB version {} [{}]'.format(caldbinfo['CALDBVER'], caldbinfo['CALDBDIR']))
|
||||
print()
|
||||
|
||||
def check(self):
|
||||
if not Dir(self.srcdir).exists():
|
||||
raise TaskError('error: srcdir not found')
|
||||
Dir(self.dstdir).check()
|
||||
Dir(self.tmpdir).check(clean=True)
|
||||
Dir(self.tmpdst).check(clean=True)
|
||||
|
||||
|
||||
class PipeConfigL0(object):
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self._layout = self._get_layout()
|
||||
|
||||
def _get_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'L0'
|
||||
layout['post'] = '_urd.L2'
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick L0 events
|
||||
ufdir = Dir(self._args.srcdir)
|
||||
eventfiles = self._list_files(ufdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
uffiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
uffiles.append((ufdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = uffiles
|
||||
|
||||
# pick orientation
|
||||
orientation = {}
|
||||
|
||||
auxdir = Dir(self._args.srcdir)
|
||||
auxfiles = self._list_files(auxdir.get(), self._args.stem_in)
|
||||
|
||||
for otype in ['gyro', 'sed1', 'sed2', 'bokz']:
|
||||
for auxf in auxfiles:
|
||||
if auxf.endswith('_{}.fits'.format(otype)):
|
||||
ofile = auxdir / auxf
|
||||
|
||||
if ofile.exists():
|
||||
orientation[otype] = ofile.get()
|
||||
|
||||
layout['orientation'] = orientation
|
||||
|
||||
return layout
|
||||
|
||||
@property
|
||||
def layout(self): return self._layout
|
||||
|
||||
|
||||
class PipeConfigPlain(object):
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self._layout = self._get_layout()
|
||||
|
||||
def _list_files(self, srcdir, stem_in=None):
|
||||
allfiles = [fi for fi in os.listdir(srcdir)]
|
||||
allfiles.sort()
|
||||
|
||||
if stem_in==None:
|
||||
return allfiles
|
||||
|
||||
return [fi for fi in allfiles if fi.startswith(stem_in)]
|
||||
|
||||
def _get_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'plain'
|
||||
layout['post'] = ''
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick events
|
||||
srcdir = Dir(self._args.srcdir)
|
||||
eventfiles = self._list_files(srcdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
telfiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
telfiles.append((srcdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = telfiles
|
||||
|
||||
return layout
|
||||
|
||||
@property
|
||||
def layout(self): return self._layout
|
||||
|
||||
|
||||
class PipeConfigScience(object):
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self._layout = self._get_layout()
|
||||
|
||||
def _list_files(self, srcdir, stem_in=None):
|
||||
allfiles = [fi for fi in os.listdir(srcdir)]
|
||||
allfiles.sort()
|
||||
|
||||
if stem_in==None:
|
||||
return allfiles
|
||||
|
||||
return [fi for fi in allfiles if fi.startswith(stem_in)]
|
||||
|
||||
def _get_layout(self):
|
||||
layout = {}
|
||||
|
||||
layout['type'] = 'science'
|
||||
layout['post'] = '_cl'
|
||||
layout['uf' ] = {}
|
||||
|
||||
# pick uf events
|
||||
ufdir = Dir(self._args.srcdir) / 'event_uf'
|
||||
eventfiles = self._list_files(ufdir.get(), self._args.stem_in)
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
uffiles = []
|
||||
for fname in eventfiles:
|
||||
if TEL in fname:
|
||||
uffiles.append((ufdir / fname).get())
|
||||
|
||||
layout['uf'][TEL] = uffiles
|
||||
|
||||
# pick orientation
|
||||
orientation = {}
|
||||
|
||||
auxdir = Dir(self._args.srcdir) / 'auxiliary'
|
||||
auxfiles = self._list_files(auxdir.get(), self._args.stem_in)
|
||||
|
||||
for otype in ['gyro', 'sed1', 'sed2', 'bokz']:
|
||||
for auxf in auxfiles:
|
||||
if auxf.endswith('_{}.fits'.format(otype)):
|
||||
ofile = auxdir / auxf
|
||||
|
||||
if ofile.exists():
|
||||
orientation[otype] = ofile.get()
|
||||
|
||||
layout['orientation'] = orientation
|
||||
|
||||
return layout
|
||||
|
||||
@property
|
||||
def layout(self): return self._layout
|
||||
|
||||
|
||||
class EventProcContext(object):
|
||||
pass
|
||||
|
||||
|
||||
class Pipeline(Task):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
print('*** artpipeline v.{}'.format(__version__))
|
||||
|
||||
self._name = 'artpipeline v.{}'.format(__version__)
|
||||
|
||||
self._args = Args()
|
||||
|
||||
self._config = None
|
||||
self._random = None
|
||||
self._ofile = None
|
||||
|
||||
def initialize(self):
|
||||
self._args.read()
|
||||
self._args.print()
|
||||
self._args.check()
|
||||
|
||||
if not self._args.no_random:
|
||||
self._random = Random(self._args.seed)
|
||||
|
||||
if self._args.plain_energy:
|
||||
self._config = PipeConfigPlain(self._args)
|
||||
elif self._args.process_l0:
|
||||
self._config = PipeConfigL0(self._args)
|
||||
else:
|
||||
self._config = PipeConfigScience(self._args)
|
||||
|
||||
def finalize(self):
|
||||
Dir(self._args.tmpdir).delete()
|
||||
|
||||
def runmain(self):
|
||||
# layout = self._layout()
|
||||
|
||||
layout = self._config.layout()
|
||||
|
||||
self._make_orient(layout)
|
||||
self._make_attitude(layout)
|
||||
|
||||
self._process_events(layout)
|
||||
|
||||
print('*** artpipeline finished')
|
||||
|
||||
def _get_stem(self, fname, sep):
|
||||
# get filenme from path
|
||||
fn = os.path.basename(fname)
|
||||
|
||||
#find teln in fname
|
||||
fi = fn.find(sep)
|
||||
|
||||
return fn[:fi]
|
||||
|
||||
def _list_files(self, srcdir, stem_in=None):
|
||||
allfiles = [fi for fi in os.listdir(srcdir)]
|
||||
allfiles.sort()
|
||||
|
||||
if stem_in==None:
|
||||
return allfiles
|
||||
|
||||
return [fi for fi in allfiles if fi.startswith(stem_in)]
|
||||
|
||||
|
||||
def _layout(self):
|
||||
if self._args.process_l0:
|
||||
return self._L0_layout()
|
||||
|
||||
if self._args.plain_energy:
|
||||
return self._plain_layout()
|
||||
|
||||
return self._science_layout()
|
||||
|
||||
def _make_orient(self, layout):
|
||||
if layout['type'] == 'plain':
|
||||
print('mode: plain: skipping orienation...')
|
||||
return
|
||||
|
||||
print('making orienation file...')
|
||||
gyrofile = layout['orientation']['gyro']
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(gyrofile, sep='_')
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
if self._args.process_l0:
|
||||
auxdir = Dir(self._args.dstdir)
|
||||
else:
|
||||
auxdir = Dir(self._args.dstdir) / 'auxiliary'
|
||||
|
||||
auxdir.check()
|
||||
|
||||
ofile = '{}_ori.fits'.format(stemout)
|
||||
ofull = (auxdir / ofile).get()
|
||||
|
||||
skyaxis = SkyAxis()
|
||||
skyaxis.process(gyrofile).write(ofull)
|
||||
|
||||
self._ofile = ofull
|
||||
|
||||
print('written: {}'.format(ofull))
|
||||
|
||||
def _make_attitude(self, layout):
|
||||
if layout['type'] == 'plain':
|
||||
print('mode: plain: skipping attitude...')
|
||||
return
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(self._orifile, sep='_')
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
print('making attitude file...')
|
||||
attfile = '{}_att.fits'.format(stemout)
|
||||
|
||||
if self._args.process_l0:
|
||||
attfull = (Dir(self._args.dstdir) / attfile).get()
|
||||
else:
|
||||
attfull = (Dir(self._args.dstdir) / 'auxiliary' / attfile).get()
|
||||
|
||||
caldb = CaldbOrientation(self._args.caldbdir, 'GYRO')
|
||||
skycoord = SkyCoord(caldb, 'GYRO')
|
||||
|
||||
skycoord.attitude(self._orifile).write(attfull)
|
||||
|
||||
print('written: {}'.format(attfull))
|
||||
|
||||
def _process_events(self, layout):
|
||||
processed = datetime.datetime.now(tz=TZ_MSK).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
metainfo = {
|
||||
'artpipeline': __version__,
|
||||
'processed' : processed
|
||||
}
|
||||
|
||||
if self._args.plain_energy:
|
||||
metainfo['mode'] = 'plain'
|
||||
elif self._args.process_l0:
|
||||
metainfo['mode'] = 'L0'
|
||||
else:
|
||||
metainfo['mode'] = 'science'
|
||||
|
||||
caldbinfo = CaldbInfo(self._args.caldbdir)
|
||||
|
||||
metainfo['caldb'] = caldbinfo.get()['CALDBVER']
|
||||
|
||||
if not self._args.no_random:
|
||||
random = self._random
|
||||
metainfo['seed'] = random.seed
|
||||
else:
|
||||
random = None
|
||||
metainfo['seed'] = None
|
||||
|
||||
metainfo['events'] = {}
|
||||
|
||||
for TEL in Teldef.telnames:
|
||||
print('processing: {}'.format(TEL))
|
||||
|
||||
for fitsname in layout['uf'].get(TEL, []):
|
||||
|
||||
print('event file: {}'.format(fitsname))
|
||||
|
||||
if self._args.stem is None:
|
||||
stemout = self._get_stem(fitsname, TEL)
|
||||
else:
|
||||
stemout = self._args.stem
|
||||
|
||||
# read events
|
||||
events = Events(fitsname).read()
|
||||
|
||||
if self._args.plain_energy:
|
||||
times = events.events.column('TIME')
|
||||
events.setcol('RA' , np.zeros_like(times))
|
||||
events.setcol('DEC', np.zeros_like(times))
|
||||
else:
|
||||
# calc ra/dec
|
||||
ocaldb = CaldbOrientation(self._args.caldbdir, 'GYRO')
|
||||
skycoord = SkyCoord(ocaldb, 'GYRO')
|
||||
|
||||
skycoord.events(self._orifile, events, random)
|
||||
|
||||
# calc energies
|
||||
ecaldb = CaldbEnergy(self._args.caldbdir, TEL)
|
||||
energy = Energy(ecaldb)
|
||||
|
||||
en, enbot, entop, grade =\
|
||||
energy.calc(events.teln, events.events, self._random)
|
||||
|
||||
events.events.setcol('ENERGY' , en )
|
||||
events.events.setcol('ENERGY_BOT', enbot)
|
||||
events.events.setcol('ENERGY_TOP', entop)
|
||||
events.events.setcol('GRADE' , grade)
|
||||
|
||||
# calc flag
|
||||
mcaldb = CaldbMask(self._args.caldbdir, TEL)
|
||||
flag = Flag(mcaldb)
|
||||
f = flag.calc(events.events, events.tstart, events.tstop)
|
||||
|
||||
events.events.setcol('FLAG', f)
|
||||
|
||||
# print(events.events.frame)
|
||||
|
||||
if self._args.plain_energy:
|
||||
stemout = self._get_stem(fitsname, '.fits')
|
||||
dstfile = '{}.L2.fits'.format(stemout)
|
||||
else:
|
||||
dstfile = '{}{}{}.fits'.format(stemout, events.telname, layout['post'])
|
||||
|
||||
if self._args.plain_energy or self._args.process_l0:
|
||||
dstdir = Dir(self._args.dstdir)
|
||||
else:
|
||||
dstdir = Dir(self._args.dstdir) / 'event_cl'
|
||||
|
||||
dstdir.check()
|
||||
|
||||
dstfull = (dstdir / dstfile).get()
|
||||
events.write_cl(dstfull, caldbinfo.get())
|
||||
|
||||
aday_start = MissionTime(events.tstart).to_artday()
|
||||
aday_stop = MissionTime(events.tstop ).to_artday()
|
||||
|
||||
metainfo['events'][dstfile] = {
|
||||
'artday' : (aday_start, aday_stop),
|
||||
'mission': (events.tstart, events.tstop),
|
||||
'events' : events.size
|
||||
}
|
||||
|
||||
print('written: {}'.format(dstfull))
|
||||
|
||||
# write meta
|
||||
metafname = (Dir(self._args.dstdir) / 'meta.info').get()
|
||||
with open(metafname, 'w') as metafile:
|
||||
json.dump(metainfo, metafile, indent=4)
|
||||
|
||||
print('written: {}'.format(metafname))
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
Pipeline().run()
|
||||
except TaskError as e:
|
||||
print('error: task execution:', e)
|
||||
except Error as e:
|
||||
print('error: unknown:', e)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
# PipelineTask().runall()
|
||||
34
artpipeline/src/artpipeline/skyaxis.py
Executable file
34
artpipeline/src/artpipeline/skyaxis.py
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2026 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
class SkyAxis(object):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._srcfile = None
|
||||
|
||||
def process(self, srcfile):
|
||||
self._srcfile = srcfile
|
||||
return self
|
||||
|
||||
def write(self, dstfile):
|
||||
|
||||
if os.path.exists(dstfile):
|
||||
os.remove(dstfile)
|
||||
|
||||
shutil.copy(self._srcfile, dstfile)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
194
artpipeline/src/artpipeline/skycoord.py
Executable file
194
artpipeline/src/artpipeline/skycoord.py
Executable file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf8 -*-
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2025 IKI RAS (http://iki.rssi.ru/)
|
||||
# Space Research Institute of the Russian Academy of Science
|
||||
#
|
||||
# This file is part of the M. Pavlinsky SRG/ART-XC software project.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from astropy.io import fits
|
||||
|
||||
from arttools.gti import Gti
|
||||
from arttools.arttime import MJDREF
|
||||
from arttools.telescope import Teldef
|
||||
|
||||
from artpipeline.orientation import Orientation
|
||||
from artpipeline.orientation import AttitudeStatus
|
||||
|
||||
from artpipeline.events import Events
|
||||
|
||||
from artpipeline.version import __version__
|
||||
|
||||
|
||||
class SkyCoord(object):
|
||||
|
||||
def __init__(self, caldb, orifile, instname=None):
|
||||
super().__init__()
|
||||
|
||||
self._caldb = caldb
|
||||
self._ofile = orifile
|
||||
|
||||
self._instname = instname
|
||||
self._inst = None
|
||||
|
||||
self._mjdref = MJDREF.mjd
|
||||
|
||||
self._data = {}
|
||||
self._adata = {}
|
||||
|
||||
self._process_orient()
|
||||
self._calc_base_attitude()
|
||||
|
||||
def _process_orient(self):
|
||||
self._orient = Orientation(self._caldb)
|
||||
self._odata = self._orient.read(self._ofile)
|
||||
self._gti = Gti.from_hduname(self._ofile, 'STDGTI')
|
||||
|
||||
self._otime = self._odata['time']
|
||||
self._ostatus = AttitudeStatus.pack(self._odata['state'])
|
||||
|
||||
def _calc_base_attitude(self):
|
||||
if self._instname is None:
|
||||
return
|
||||
|
||||
att_ra, att_dec, att_roll \
|
||||
= self._orient.calc_attitudes(self._odata, self._instname)
|
||||
|
||||
self._data['ATTITUDE'] = {
|
||||
'time' : self._otime,
|
||||
'ra' : att_ra ,
|
||||
'dec' : att_dec ,
|
||||
'roll' : att_roll ,
|
||||
'status': self._ostatus
|
||||
}
|
||||
|
||||
x_ra, x_dec, x_roll \
|
||||
= self._orient.calc_attitudes(self._odata)
|
||||
|
||||
self._data['SCX'] = {
|
||||
'time' : self._otime,
|
||||
'ra' : x_ra ,
|
||||
'dec' : x_dec ,
|
||||
'roll' : x_roll ,
|
||||
'status': self._ostatus
|
||||
}
|
||||
|
||||
def attitude(self, instname=None):
|
||||
|
||||
if instname is not None:
|
||||
self._inst = instname
|
||||
else:
|
||||
self._inst = self._instname
|
||||
|
||||
ra, dec, roll \
|
||||
= self._orient.calc_attitudes(self._odata, self._inst)
|
||||
|
||||
self._adata[self._inst] = {
|
||||
'time' : self._otime,
|
||||
'ra' : ra ,
|
||||
'dec' : dec ,
|
||||
'roll' : roll ,
|
||||
'status': self._ostatus
|
||||
}
|
||||
|
||||
return self
|
||||
|
||||
def collect(self, name=None):
|
||||
if self._inst is None:
|
||||
return
|
||||
|
||||
if name is None:
|
||||
name = self._inst
|
||||
|
||||
self._data[name] = self._adata.pop(self._inst, None)
|
||||
self._inst = None
|
||||
|
||||
def _make_hdu(self, hduname):
|
||||
|
||||
if hduname not in self._data:
|
||||
|
||||
hdu = fits.BinTableHDU()
|
||||
hdu.name = hduname
|
||||
|
||||
return hdu
|
||||
|
||||
# --- HDU ---
|
||||
a00 = np.array(self._data[hduname]['time' ], dtype=np.double)
|
||||
a01 = np.array(self._data[hduname]['ra' ], dtype=np.double)
|
||||
a02 = np.array(self._data[hduname]['dec' ], dtype=np.double)
|
||||
a03 = np.array(self._data[hduname]['roll' ], dtype=np.double)
|
||||
a04 = np.array(self._data[hduname]['status'], dtype=int )
|
||||
a05 = np.zeros(a00.size, dtype=int)
|
||||
|
||||
c00 = fits.Column(name='TIME' , format='1D', unit='sec', array=a00)
|
||||
c01 = fits.Column(name='RA' , format='1D', unit='deg', array=a01)
|
||||
c02 = fits.Column(name='DEC' , format='1D', unit='deg', array=a02)
|
||||
c03 = fits.Column(name='ROLL' , format='1D', unit='deg', array=a03)
|
||||
c04 = fits.Column(name='STATUS', format='1I', unit='' , array=a04)
|
||||
c05 = fits.Column(name='FLAG' , format='1I', unit='' , array=a05)
|
||||
|
||||
cols = fits.ColDefs([
|
||||
c00, c01, c02, c03, c04, c05
|
||||
])
|
||||
|
||||
hdu = fits.BinTableHDU.from_columns(cols)
|
||||
hdu.name = hduname
|
||||
|
||||
creator = 'artpipeline v.{}'.format(__version__)
|
||||
|
||||
hdu.header['CREATOR' ] = (creator , 'program and version that created this file' )
|
||||
hdu.header['ORIGIN' ] = ('IKI RAS' , 'institution that created this file' )
|
||||
hdu.header['TELESCOP'] = ('SRG/ART-XC', 'mission name' )
|
||||
hdu.header['MJDREF' ] = (self._mjdref, 'MJD corresponding to SC clock start 2000.0 MSK')
|
||||
hdu.header['TSTART' ] = self._gti.start
|
||||
hdu.header['TSTOP' ] = self._gti.stop
|
||||
hdu.header['CALDBVER'] = '{}'.format(self._caldb.version())
|
||||
|
||||
return hdu
|
||||
|
||||
def write(self, attfile):
|
||||
|
||||
print(self._data.keys())
|
||||
|
||||
prihdu = fits.PrimaryHDU()
|
||||
|
||||
# --- make file ---
|
||||
hdulist = fits.HDUList([
|
||||
prihdu,
|
||||
self._make_hdu('ATTITUDE'),
|
||||
self._make_hdu('T1' ),
|
||||
self._make_hdu('T2' ),
|
||||
self._make_hdu('T3' ),
|
||||
self._make_hdu('T4' ),
|
||||
self._make_hdu('T5' ),
|
||||
self._make_hdu('T6' ),
|
||||
self._make_hdu('T7' ),
|
||||
self._make_hdu('SCX' ),
|
||||
self._gti.make_hdu(self._mjdref)
|
||||
])
|
||||
|
||||
hdulist.writeto(attfile, overwrite=True, checksum=True)
|
||||
|
||||
|
||||
def events(self, orifile, events, random=None):
|
||||
ra, dec, mask \
|
||||
= self._orient.calc_events_coords(
|
||||
self._odata, events.events, events.telname, random)
|
||||
|
||||
|
||||
flag = np.zeros_like(events.events.column('TIME' ))
|
||||
flag[~mask] = 100
|
||||
|
||||
events.setcol('RA' , ra )
|
||||
events.setcol('DEC' , dec )
|
||||
|
||||
return flag
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
1
artpipeline/src/artpipeline/version.py
Executable file
1
artpipeline/src/artpipeline/version.py
Executable file
@@ -0,0 +1 @@
|
||||
__version__ = '2.1.0'
|
||||
Reference in New Issue
Block a user