GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GPulsar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GPulsar.cpp - Pulsar class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GPulsar.cpp
23  * @brief Pulsar class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio> // std::fopen, std::fgets, std::fclose, etc...
32 #include "GPulsar.hpp"
33 #include "GFits.hpp"
34 #include "GGti.hpp"
35 #include "GEphemerides.hpp"
36 #include "GFilename.hpp"
37 
38 /* __ Method name definitions ____________________________________________ */
39 #define G_AT "GPulsarEphemeris& GPulsar::at(int&)"
40 #define G_EPHEMERIS "GPulsar::ephemeris(GTime&)"
41 #define G_LOAD "GPulsar::load(GFilename&, std::string&)"
42 #define G_LOAD_FITS "GPulsar::load_fits(GFilename&, std::string&)"
43 #define G_LOAD_INTEGRAL "GPulsar::load_integral(GFitsTable*, std::string&)"
44 #define G_LOAD_FERMI "GPulsar::load_fermi(GFitsTable*, std::string&)"
45 #define G_LOAD_PSRTIME "GPulsar::load_psrtime(GFilename&, std::string&)"
46 #define G_LOAD_PARFILE "GPulsar::load_parfile(GFilename&)"
47 
48 /* __ Macros _____________________________________________________________ */
49 
50 /* __ Coding definitions _________________________________________________ */
51 
52 /* __ Debug definitions __________________________________________________ */
53 
54 
55 
56 /*==========================================================================
57  = =
58  = Constructors/destructors =
59  = =
60  ==========================================================================*/
61 
62 /***********************************************************************//**
63  * @brief Void constructor
64  ***************************************************************************/
66 {
67  // Initialise class members
68  init_members();
69 
70  // Return
71  return;
72 }
73 
74 
75 /***********************************************************************//**
76  * @brief Filename constructor
77  *
78  * @param[in] filename File name of pulsar ephemerides
79  * @param[in] name Pulsar name
80  *
81  * Constructs a pulsar from an ephemerides file. In case that several pulsars
82  * are present in the specified ephemerides file the name of the pulsar that
83  * should be constructed is to be specified.
84  ***************************************************************************/
85 GPulsar::GPulsar(const GFilename& filename, const std::string& name)
86 {
87  // Initialise class members
88  init_members();
89 
90  // Load pulsar ephemerides
91  load(filename, name);
92 
93  // Return
94  return;
95 }
96 
97 
98 /***********************************************************************//**
99  * @brief Copy constructor
100  *
101  * @param[in] pulsar Pulsar.
102  ***************************************************************************/
103 GPulsar::GPulsar(const GPulsar& pulsar)
104 {
105  // Initialise class members
106  init_members();
107 
108  // Copy members
109  copy_members(pulsar);
110 
111  // Return
112  return;
113 }
114 
115 
116 /***********************************************************************//**
117  * @brief Destructor
118  ***************************************************************************/
120 {
121  // Free members
122  free_members();
123 
124  // Return
125  return;
126 }
127 
128 
129 /*==========================================================================
130  = =
131  = Operators =
132  = =
133  ==========================================================================*/
134 
135 /***********************************************************************//**
136  * @brief Assignment operator
137  *
138  * @param[in] pulsar Pulsar.
139  * @return Pulsar.
140  ***************************************************************************/
142 {
143  // Execute only if object is not identical
144  if (this != &pulsar) {
145 
146  // Free members
147  free_members();
148 
149  // Initialise private members
150  init_members();
151 
152  // Copy members
153  copy_members(pulsar);
154 
155  } // endif: object was not identical
156 
157  // Return this object
158  return *this;
159 }
160 
161 
162 /*==========================================================================
163  = =
164  = Public methods =
165  = =
166  ==========================================================================*/
167 
168 /***********************************************************************//**
169  * @brief Clear Pulsar
170  ***************************************************************************/
171 void GPulsar::clear(void)
172 {
173  // Free members
174  free_members();
175 
176  // Initialise private members
177  init_members();
178 
179  // Return
180  return;
181 }
182 
183 
184 /***********************************************************************//**
185  * @brief Clone Pulsar
186  *
187  * @return Pointer to deep copy of Pulsar.
188  ***************************************************************************/
190 {
191  return new GPulsar(*this);
192 }
193 
194 /***********************************************************************//**
195  * @brief Return reference to ephemeris
196  *
197  * @param[in] index Ephemeris index [0,...,size()-1].
198  *
199  * @exception GException::out_of_range
200  * Ephemeris index is out of range.
201  *
202  * Returns a reference to the ephemeris with the specified @p index.
203  ***************************************************************************/
204 GPulsarEphemeris& GPulsar::at(const int& index)
205 {
206  // Compile option: raise an exception if index is out of range
207  #if defined(G_RANGE_CHECK)
208  if (index < 0 || index >= size()) {
209  throw GException::out_of_range(G_AT, "Ephemeris index", index, size());
210  }
211  #endif
212 
213  // Return reference
214  return (m_ephemerides[index]);
215 }
216 
217 
218 /***********************************************************************//**
219  * @brief Return reference to ephemeris (const version)
220  *
221  * @param[in] index Ephemeris index [0,...,size()-1].
222  *
223  * @exception GException::out_of_range
224  * Ephemeris index is out of range.
225  *
226  * Returns a const reference to the ephemeris with the specified @p index.
227  ***************************************************************************/
228 const GPulsarEphemeris& GPulsar::at(const int& index) const
229 {
230  // Compile option: raise an exception if index is out of range
231  #if defined(G_RANGE_CHECK)
232  if (index < 0 || index >= size()) {
233  throw GException::out_of_range(G_AT, "Ephemeris index", index, size());
234  }
235  #endif
236 
237  // Return reference
238  return (m_ephemerides[index]);
239 }
240 
241 
242 /***********************************************************************//**
243  * @brief Return pulsar ephemeris
244  *
245  * param[in] time Time.
246  * @return Pulsar ephemeris.
247  *
248  * @exception GException::invalid_argument
249  * No valid ephemeris found for specified @p time.
250  *
251  * Returns the pulsar ephemeris as function of time.
252  ***************************************************************************/
253 const GPulsarEphemeris& GPulsar::ephemeris(const GTime& time) const
254 {
255  // Find appropriate ephemerides
256  int ifound = -1;
257  for (int i = 0; i < size(); ++i) {
258  if (time >= m_ephemerides[i].tstart() &&
259  time <= m_ephemerides[i].tstop()) {
260  ifound = i;
261  break;
262  }
263  }
264 
265  // Throw an expection if no valid ephemerides were found
266  if (ifound == -1) {
267  std::string msg = "No valid ephemeris found for MJD "+
268  gammalib::str(time.mjd())+". Please specify "
269  "ephemerides that comprise the time.";
271  }
272 
273  // Return ephemerides
274  return (m_ephemerides[ifound]);
275 }
276 
277 
278 /***********************************************************************//**
279  * @brief Return validity intervals of pulsar ephemerides
280  *
281  * @return Validity intervals of pulsar ephemerides.
282  *
283  * Returns the validity intervals of pulsar ephemerides as Good Time
284  * Intervals.
285  ***************************************************************************/
287 {
288  // Initialise Good Time Intervals
289  GGti gti;
290 
291  // Loop over ephemerides
292  for (int i = 0; i < size(); ++i) {
293  gti.append(m_ephemerides[i].tstart(), m_ephemerides[i].tstop());
294  }
295 
296  // Return Good Time Intervals
297  return gti;
298 }
299 
300 
301 /***********************************************************************//**
302  * @brief Load Pulsar from ephemerides file
303  *
304  * @param[in] filename File name of pulsar ephemerides
305  * @param[in] name Pulsar name
306  *
307  * @exception GException::file_error
308  * Could not open specified file as an ASCII file
309  *
310  * Load a pulsar from an ephemerides file. In case that several pulsars are
311  * present in the specified ephemerides file the name of the pulsar that
312  * should be constructed is to be specified.
313  ***************************************************************************/
314 void GPulsar::load(const GFilename& filename, const std::string& name)
315 {
316  // Clear pulsar
317  clear();
318 
319  // If filename is a FITS file then load ephemerides from a FITS file
320  if (filename.is_fits()) {
321  load_fits(filename, name);
322  }
323 
324  // ... otherwise we expect that file is an ASCII file and we dispatch
325  // according to ASCII file content
326  else {
327 
328  // Allocate line buffer
329  const int n = 1000;
330  char line[n];
331 
332  // Open file as ASCII file
333  FILE* fptr = std::fopen(filename.url().c_str(), "r");
334  if (fptr == NULL) {
335  std::string msg = "Pulsar ephemerides file \""+filename.url()+
336  "\" not found or readable. Please specify a "
337  "valid and readable ephemerides file.";
338  throw GException::file_error(G_LOAD, msg);
339  }
340 
341  // Read first line in buffer
342  if (std::fgets(line, n, fptr) != NULL) {
343 
344  // Close file as we don't need it anymore
345  std::fclose(fptr);
346 
347  // Split line in elements
348  std::vector<std::string> elements = gammalib::split(line, " ");
349 
350  // If there are many elements in line we deal with a file in
351  // psrtime format
352  if (elements.size() > 10) {
353  load_psrtime(filename, name);
354  }
355 
356  // ... otherwise we deal with a tempo2 par file
357  else {
358  load_parfile(filename);
359  }
360 
361  } // endif: there was a line in the file
362 
363  // ... otherwise the file was empty and we simply close it
364  else {
365  std::fclose(fptr);
366  }
367 
368  } // endelse: handled ASCII files
369 
370  // Return
371  return;
372 }
373 
374 
375 /***********************************************************************//**
376  * @brief Print Pulsar
377  *
378  * @param[in] chatter Chattiness.
379  * @return String containing Pulsar information.
380  ***************************************************************************/
381 std::string GPulsar::print(const GChatter& chatter) const
382 {
383  // Initialise result string
384  std::string result;
385 
386  // Continue only if chatter is not silent
387  if (chatter != SILENT) {
388 
389  // Get ephemerides validity interval
390  GGti gti = validity();
391 
392  // Append header
393  result.append("=== GPulsar ===");
394 
395  // Append information
396  result.append("\n"+gammalib::parformat("Pulsar name"));
397  result.append(m_name);
398  result.append("\n"+gammalib::parformat("Number of ephemerides"));
399  result.append(gammalib::str(m_ephemerides.size()));
400  if (!is_empty()) {
401  result.append("\n"+gammalib::parformat("Validity MJD range"));
402  result.append(gammalib::str(gti.tstart().mjd()));
403  result.append(" - ");
404  result.append(gammalib::str(gti.tstop().mjd()));
405  result.append("\n"+gammalib::parformat("Validity UTC range"));
406  result.append(gti.tstart().utc());
407  result.append(" - ");
408  result.append(gti.tstop().utc());
409  }
410 
411  // EXPLICIT: Append ephemerides
412  if (chatter >= EXPLICIT) {
413  for (int i = 0; i < m_ephemerides.size(); ++i) {
414  result.append("\n"+m_ephemerides[i].print());
415  }
416  }
417 
418  } // endif: chatter was not silent
419 
420  // Return result
421  return result;
422 }
423 
424 
425 /*==========================================================================
426  = =
427  = Private methods =
428  = =
429  ==========================================================================*/
430 
431 /***********************************************************************//**
432  * @brief Initialise class members
433  ***************************************************************************/
435 {
436  // Initialise members
437  m_name.clear();
438  m_ephemerides.clear();
439 
440  // Return
441  return;
442 }
443 
444 
445 /***********************************************************************//**
446  * @brief Copy class members
447  *
448  * @param[in] pulsar Pulsar.
449  ***************************************************************************/
450 void GPulsar::copy_members(const GPulsar& pulsar)
451 {
452  // Copy members
453  m_name = pulsar.m_name;
454  m_ephemerides = pulsar.m_ephemerides;
455 
456  // Return
457  return;
458 }
459 
460 
461 /***********************************************************************//**
462  * @brief Delete class members
463  ***************************************************************************/
465 {
466  // Return
467  return;
468 }
469 
470 
471 /***********************************************************************//**
472  * @brief Load Pulsar from ephemerides FITS file
473  *
474  * @param[in] filename Name of pulsar ephemerides FITS file
475  * @param[in] name Pulsar name
476  *
477  * @exception GException::file_error
478  * FITS file format not recognised
479  *
480  * Load a pulsar from an ephemerides FITS file. In case that several pulsars
481  * are present in the specified ephemerides file the name of the pulsar that
482  * should be constructed is to be specified.
483  ***************************************************************************/
484 void GPulsar::load_fits(const GFilename& filename, const std::string& name)
485 {
486  // Open FITS file
487  GFits fits(filename);
488 
489  // If FITS file is an INTEGRAL file then load ephemerides from INTEGRAL
490  // FITS table
491  if (fits.contains("GNRL-EPHE-CAT")) {
492  const GFitsTable* table = fits.table("GNRL-EPHE-CAT");
493  load_integral(table, name);
494  }
495 
496  // ... otherwise if FITS file is a Fermi D4 file then load ephemerides
497  // from Fermi D4 table
498  else if (fits.contains("SPIN_PARAMETERS")) {
499  const GFitsTable* table = fits.table("SPIN_PARAMETERS");
500  load_fermi(table, name);
501  }
502 
503  // ... otherwise signal that the FITS file was not recognised
504  else {
505  std::string msg = "Could not recognised format of FITS file \""+
506  filename.url()+"\". Please specify either an "
507  "INTEGRAL or a D4 Fermi file.";
509  }
510 
511  // Return
512  return;
513 }
514 
515 
516 /***********************************************************************//**
517  * @brief Load Pulsar from INTEGRAL ephemerides FITS table
518  *
519  * @param[in] table INTEGRAL ephemerides FITS table
520  * @param[in] name Pulsar name
521  *
522  * @exception GException::invalid_argument
523  * Specified pulsar name not found in FITS table
524  *
525  * Load a pulsar from INTEGRAL ephemerides FITS table. In the INTEGRAL format
526  * only a single pulsar will be present in the FITS file, specified by
527  * the SOURCEID keyword.
528  ***************************************************************************/
529 void GPulsar::load_integral(const GFitsTable* table, const std::string& name)
530 {
531  // Get pulsar attributes from FITS table
532  m_name = table->string("SOURCEID");
533  double ra = table->real("RA_OBJ");
534  double dec = table->real("DEC_OBJ");
535 
536  // Check name if provided
537  if (!name.empty()) {
538  if (name != m_name) {
539  std::string msg = "Pulsar \""+m_name+"\" found in ephemerides "
540  "file yet pulsar \""+name+"\" was requested. "
541  "Please check the requested pulsar name.";
543  }
544  }
545 
546  // Set sky direction
547  GSkyDir dir;
548  dir.radec_deg(ra, dec);
549 
550  // Get pointers to table columns
551  const GFitsTableCol* col_mjdstart = (*table)["MJDSTART"];
552  const GFitsTableCol* col_mjdstop = (*table)["MJDSTOP"];
553  const GFitsTableCol* col_tref0 = (*table)["TREF0"];
554  const GFitsTableCol* col_t2peak = (*table)["T2PEAK"];
555  const GFitsTableCol* col_f0 = (*table)["F0"];
556  const GFitsTableCol* col_f1 = (*table)["FDOT0"];
557  const GFitsTableCol* col_f2 = (*table)["FDOTDOT0"];
558 
559  // Extract ephemerides
560  for (int i = 0; i < col_mjdstart->nrows(); ++i) {
561 
562  // Set validity interval
563  GTime tstart;
564  GTime tstop;
565  tstart.mjd(col_mjdstart->real(i));
566  tstop.mjd(col_mjdstop->real(i));
567 
568  // Set t0. The interpretation of the FITS table information has been
569  // validated by inspecting the code in the INTEGRAL OSA task
570  // spi_phase_hist and specifically the file spi_phase_hist.cpp which
571  // defines the Ephem::load method that loads an ephemeris from a
572  // GNRL-EPHE-CAT extension. The computation of a pulsar phase from
573  // ephemeris data is implemented in Evt::phase, confirming that
574  // T2PEAK is to be added to TREF0 to define the pulsar reference
575  // time. The original code is
576  //
577  // double deltaT = 86400.0 * ( _orbi - ephem.tref0 ) - ephem.t2peak0;
578  //
579  // taking into account that tref0 is given in days and t2peak0 is
580  // given in seconds. _orbi is the time in days in the above code.
581  GTime t0;
582  t0.mjd(double(int(col_tref0->real(i))));
583  //t0 += col_t2peak->real(i);
584 
585  // Get frequency and derivatives
586  double f0 = col_f0->real(i);
587  double f1 = col_f1->real(i);
588  double f2 = col_f2->real(i);
589 
590  // Compute phase of first pulse
591  const double c1 = 1.0/2.0;
592  const double c2 = 1.0/6.0;
593  double dt = col_t2peak->real(i);
594  double phase = -((f0 + (f1 * c1 + f2 * dt * c2) * dt) * dt);
595  phase -= floor(phase);
596 
597  // Allocate ephemeris
599 
600  // Set attributes
601  ephemeris.name(m_name);
602  ephemeris.dir(dir);
603  ephemeris.tstart(tstart);
604  ephemeris.tstop(tstop);
605  ephemeris.t0(t0);
606  ephemeris.phase(phase);
607  ephemeris.f0(f0);
608  ephemeris.f1(f1);
609  ephemeris.f2(f2);
610 
611  // Push back ephemeris
612  m_ephemerides.push_back(ephemeris);
613 
614  } // endfor: looped over ephemerides
615 
616  // Return
617  return;
618 }
619 
620 
621 /***********************************************************************//**
622  * @brief Load Pulsar from Fermi ephemerides FITS table
623  *
624  * @param[in] table Fermi ephemerides FITS table
625  * @param[in] name Pulsar name
626  *
627  * @exception GException::invalid_argument
628  * Specified pulsar name not found in FITS table
629  *
630  * Load a pulsar from Fermi ephemerides FITS table.
631  ***************************************************************************/
632 void GPulsar::load_fermi(const GFitsTable* table, const std::string& name)
633 {
634  // Get pointers to table columns
635  const GFitsTableCol* col_psrname = (*table)["PSRNAME"];
636  const GFitsTableCol* col_ra = (*table)["RA"];
637  const GFitsTableCol* col_dec = (*table)["DEC"];
638  const GFitsTableCol* col_valid_since = (*table)["VALID_SINCE"];
639  const GFitsTableCol* col_valid_until = (*table)["VALID_UNTIL"];
640  const GFitsTableCol* col_toabary_int = (*table)["TOABARY_INT"];
641  const GFitsTableCol* col_toabary_frac = (*table)["TOABARY_FRAC"];
642  const GFitsTableCol* col_f0 = (*table)["F0"];
643  const GFitsTableCol* col_f1 = (*table)["F1"];
644  const GFitsTableCol* col_f2 = (*table)["F2"];
645 
646  // Extract ephemerides
647  for (int i = 0; i < col_psrname->nrows(); ++i) {
648 
649  // Skip if pulsar name does not correspond to specified name
650  if (col_psrname->string(i) != name) {
651  continue;
652  }
653 
654  // Save the pulsar name
655  m_name = col_psrname->string(i);
656 
657  // Set sky direction
658  GSkyDir dir;
659  dir.radec_deg(col_ra->real(i), col_dec->real(i));
660 
661  // Set validity interval
662  GTime tstart;
663  GTime tstop;
664  tstart.mjd(col_valid_since->real(i));
665  tstop.mjd(col_valid_until->real(i));
666 
667  // Set t0
668  GTime t0;
669  //t0.mjd(col_toabary_int->integer(i)+col_toabary_frac->real(i));
670  t0.mjd(double(col_toabary_int->integer(i)));
671 
672  // Get frequency and derivatives
673  double f0 = col_f0->real(i);
674  double f1 = col_f1->real(i);
675  double f2 = col_f2->real(i);
676 
677  // Compute phase of first pulse
678  const double c1 = 1.0/2.0;
679  const double c2 = 1.0/6.0;
680  double dt = col_toabary_frac->real(i) * gammalib::sec_in_day;
681  double phase = -((f0 + (f1 * c1 + f2 * dt * c2) * dt) * dt);
682  phase -= floor(phase);
683 
684  // Allocate ephemeris
686 
687  // Set attributes
688  ephemeris.name(m_name);
689  ephemeris.dir(dir);
690  ephemeris.tstart(tstart);
691  ephemeris.tstop(tstop);
692  ephemeris.t0(t0);
693  ephemeris.phase(phase);
694  ephemeris.f0(f0);
695  ephemeris.f1(f1);
696  ephemeris.f2(f2);
697 
698  // Push back ephemeris
699  m_ephemerides.push_back(ephemeris);
700 
701  } // endfor: looped over ephemerides
702 
703  // Signal if no pulsar was found
704  if (m_ephemerides.empty()) {
705  if (name.empty()) {
706  std::string msg = "No pulsar name was specified. Please specify "
707  "the name of the pulsar for which ephemerides "
708  "should be extracted from the file.";
710  }
711  else {
712  std::string msg = "No pulsar \""+name+"\" found in ephemerides "
713  "file. Please check the specified file or the "
714  "requested pulsar name.";
716  }
717  }
718 
719  // Return
720  return;
721 }
722 
723 
724 /***********************************************************************//**
725  * @brief Load Pulsar from ephemerides psrtime file
726  *
727  * @param[in] filename Name of pulsar ephemerides psrtime file
728  * @param[in] name Pulsar name
729  *
730  * @exception GException::file_error
731  * Could not open ephemerides ASCII file
732  * @exception GException::invalid_argument
733  * No pulsar name specified for a file that contains multiple
734  * pulsars
735  *
736  * Load a pulsar from an ephemerides psrtime file. In case that several
737  * pulsars are present in the specified ephemerides file the name of the
738  * pulsar that should be constructed is to be specified.
739  ***************************************************************************/
740 void GPulsar::load_psrtime(const GFilename& filename, const std::string& name)
741 {
742  // Allocate line buffer
743  const int n = 1000;
744  char line[n];
745 
746  // Initialise ephemerides
747  GEphemerides ephemerides;
748 
749  // Open file
750  FILE* fptr = std::fopen(filename.url().c_str(), "r");
751  if (fptr == NULL) {
752  std::string msg = "Pulsar ephemerides file \""+filename.url()+
753  "\" not found or readable. Please specify a "
754  "valid and readable ephemerides file.";
756  }
757 
758  // Initialise used pulsar name
759  std::string psrname;
760 
761  // Read lines
762  while (std::fgets(line, n, fptr) != NULL) {
763 
764  // Split line in elements
765  std::vector<std::string> elements = gammalib::split(line, " ");
766 
767  // Get pulsar name
768  std::string psrb = elements[0];
769 
770  // Skip if pulsar name does not correspond to specified name
771  if (!name.empty()) {
772  std::string psrb_alt = "PSR B" + psrb;
773  if ((psrb != name) && (psrb_alt != name)) {
774  continue;
775  }
776  }
777 
778  // ... otherwise if no pulsar name was specified then check
779  // that the file only contains a single pulsar name
780  else {
781  if (!psrname.empty() && (psrb != psrname)) {
782  std::string msg = "Pulsar ephemerides file \""+filename.url()+
783  "\" contains several pulsar but not pulsar "
784  "name was specified. Please specify for which "
785  "pulsar you want to load ephemerides.";
787  }
788  }
789 
790  // Save the pulsar name
791  psrname = psrb;
792  m_name = "PSR B" + psrb;
793 
794  // Convert elements in attributes
795  double ra_h = gammalib::todouble(elements[1]);
796  double ra_m = gammalib::todouble(elements[2]);
797  double ra_s = gammalib::todouble(elements[3]);
798  double dec_d = gammalib::todouble(elements[4]);
799  double dec_m = gammalib::todouble(elements[5]);
800  double dec_s = gammalib::todouble(elements[6]);
801  double mjdstart = gammalib::todouble(elements[7]);
802  double mjdstop = gammalib::todouble(elements[8]);
803  double t0geo = gammalib::todouble(elements[9]);
804  double f0 = gammalib::todouble(gammalib::replace_segment(elements[10],"D","E"));
805  double f1 = gammalib::todouble(gammalib::replace_segment(elements[11],"D","E"));
806  double f2 = gammalib::todouble(gammalib::replace_segment(elements[12],"D","E"));
807 
808  // Compute Right Ascension and Declination
809  double ra = (ra_h + ra_m/60.0 + ra_s/3600.0) * 15.0;
810  double dec = (dec_d < 0.0) ? -(-dec_d + dec_m/60.0 + dec_s/3600.0)
811  : (dec_d + dec_m/60.0 + dec_s/3600.0);
812 
813  // Set sky direction
814  GSkyDir dir;
815  dir.radec_deg(ra, dec);
816 
817  // Set validity interval
818  GTime tstart;
819  GTime tstop;
820  tstart.mjd(mjdstart);
821  tstop.mjd(mjdstop);
822 
823  // Compute the barycentric (TDB) epoch of the spin parameters
824  double t0nom = double(int(t0geo));
825 
826  // Set t0 = t0nom
827  GTime t0;
828  t0.mjd(t0nom, "UTC");
829 
830  // Set infinite-frequency geocentric UTC arrival time of a pulse
831  GTime toa;
832  toa.mjd(t0geo, "UTC");
833 
834  // Compute phase of first pulse according to formulae given in
835  // COM-RP-DOL-DRG-065. Note that the phase is negative, see for
836  // example Eq. (3) in Yan et al. (2017), ApJ, 845, 119
837  double geo2ssb = ephemerides.geo2ssb(dir, toa);
838  double utc2tt = toa.utc2tt();
839  double dt = (t0geo - t0nom) * gammalib::sec_in_day + geo2ssb + utc2tt;
840  const double c1 = 1.0/2.0;
841  const double c2 = 1.0/6.0;
842  double phase = ((f0 + (f1 * c1 + f2 * dt * c2) * dt) * dt);
843  phase -= floor(phase);
844 
845  // Allocate ephemeris
847 
848  // Set attributes
849  ephemeris.name(m_name);
850  ephemeris.dir(dir);
851  ephemeris.tstart(tstart);
852  ephemeris.tstop(tstop);
853  ephemeris.timesys("UTC"); // psrtime ephemerides are in UTC
854  ephemeris.t0(t0);
855  ephemeris.phase(-phase);
856  ephemeris.f0(f0);
857  ephemeris.f1(f1);
858  ephemeris.f2(f2);
859 
860  // Push back ephemeris
861  m_ephemerides.push_back(ephemeris);
862 
863  } // endwhile: looped over lines
864 
865  // Close file
866  std::fclose(fptr);
867 
868  // Return
869  return;
870 }
871 
872 
873 /***********************************************************************//**
874  * @brief Load Pulsar from ephemeris par file
875  *
876  * @param[in] filename Name of pulsar ephemeris par file
877  *
878  * @exception GException::file_error
879  * Could not open ephemeris ASCII file
880  * @exception GException::invalid_argument
881  * No valid Right Ascension and Declination found
882  *
883  * Load a pulsar from an ephemeris par file.
884  ***************************************************************************/
885 void GPulsar::load_parfile(const GFilename& filename)
886 {
887  // Allocate line buffer
888  const int n = 1000;
889  char line[n];
890 
891  // Open file
892  FILE* fptr = std::fopen(filename.url().c_str(), "r");
893  if (fptr == NULL) {
894  std::string msg = "Pulsar ephemeris file \""+filename.url()+
895  "\" not found or readable. Please specify a "
896  "valid and readable ephemeris file.";
898  }
899 
900  // Allocate ephemeris
902  double ra = 9999.0;
903  double dec = 9999.0;
904 
905  // Read lines
906  while (std::fgets(line, n, fptr) != NULL) {
907 
908  // Split line in elements
909  std::vector<std::string> elements = gammalib::split(line, " \t");
910 
911  // Find index of second non-empty element
912  int i = 1;
913  for (; i < elements.size(); ++i) {
914  if (!elements[i].empty()) {
915  break;
916  }
917  }
918  if (i >= elements.size()) {
919  continue;
920  }
921 
922  // Extract keywords
923  if (elements[0] == "PSRJ") {
924  m_name = "PSR " + gammalib::rstrip_chars(elements[i], "\n");
925  ephemeris.name(m_name);
926  }
927  else if (elements[0] == "RAJ") {
928  std::vector<std::string> ras = gammalib::split(elements[i], ":");
929  double ra_h = gammalib::todouble(ras[0]);
930  double ra_m = gammalib::todouble(ras[1]);
931  double ra_s = gammalib::todouble(ras[2]);
932  ra = (ra_h + ra_m/60.0 + ra_s/3600.0) * 15.0;
933  }
934  else if (elements[0] == "DECJ") {
935  std::vector<std::string> decs = gammalib::split(elements[i], ":");
936  double dec_d = gammalib::todouble(decs[0]);
937  double dec_m = gammalib::todouble(decs[1]);
938  double dec_s = gammalib::todouble(decs[2]);
939  dec = (dec_d < 0.0) ? -(-dec_d + dec_m/60.0 + dec_s/3600.0)
940  : (dec_d + dec_m/60.0 + dec_s/3600.0);
941  }
942  else if (elements[0] == "START") {
943  GTime tstart;
944  tstart.mjd(gammalib::todouble(elements[i]));
945  ephemeris.tstart(tstart);
946  }
947  else if (elements[0] == "FINISH") {
948  GTime tstop;
949  tstop.mjd(gammalib::todouble(elements[i]));
950  ephemeris.tstop(tstop);
951  }
952  else if (elements[0] == "PEPOCH") { // Period epoch
953  GTime t0;
954  t0.mjd(gammalib::todouble(elements[i]));
955  ephemeris.t0(t0);
956  }
957  else if (elements[0] == "F0") {
958  ephemeris.f0(gammalib::todouble(elements[i]));
959  }
960  else if (elements[0] == "F1") {
961  ephemeris.f1(gammalib::todouble(elements[i]));
962  }
963  else if (elements[0] == "F2") {
964  ephemeris.f2(gammalib::todouble(elements[i]));
965  }
966 
967  } // endwhile: looped over lines
968 
969  // If Right Ascension and Declination is valid then set sky direction
970  // and push back ephemeris
971  if (ra >= 0.0 && ra <= 360.0 && dec >= -90.0 && dec <= 90.0) {
972  GSkyDir dir;
973  dir.radec_deg(ra, dec);
974  ephemeris.dir(dir);
975  m_ephemerides.push_back(ephemeris);
976  }
977 
978  // ... otherwise throw an exception
979  else {
980  std::string msg = "Right Ascension "+gammalib::str(ra)+
981  " or Declination "+gammalib::str(dec)+" in "
982  "pulsar ephemeris file \""+filename.url()+
983  "\" are invalid. Please verify the ephemeris file.";
985  }
986 
987  // Close file
988  std::fclose(fptr);
989 
990  // Return
991  return;
992 }
double f2(void) const
Returns pulsar second frequency derivative (s^-3)
std::string replace_segment(const std::string &arg, const std::string &segment, const std::string &replacement)
Replace string segment in string.
Definition: GTools.cpp:168
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition: GFits.cpp:482
virtual ~GPulsar(void)
Destructor.
Definition: GPulsar.cpp:119
void load(const GFilename &filename, const std::string &name="")
Load Pulsar from ephemerides file.
Definition: GPulsar.cpp:314
bool contains(const int &extno) const
Check if HDU exists in FITS file.
Definition: GFits.hpp:282
#define G_LOAD_FERMI
Definition: GPulsar.cpp:44
void load_fits(const GFilename &filename, const std::string &name="")
Load Pulsar from ephemerides FITS file.
Definition: GPulsar.cpp:484
#define G_LOAD_PSRTIME
Definition: GPulsar.cpp:45
#define G_LOAD_PARFILE
Definition: GPulsar.cpp:46
const std::string & name(void) const
Returns pulsar name.
void free_members(void)
Delete class members.
Definition: GPulsar.cpp:464
const GSkyDir & dir(void) const
Returns pulsar sky direction.
GTime t0(void) const
Returns reference epoch of pulsar ephemeris.
void nrows(const int &nrows)
Set number of rows in column.
void copy_members(const GPulsar &pulsar)
Copy class members.
Definition: GPulsar.cpp:450
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition: GTools.cpp:983
Time class.
Definition: GTime.hpp:55
FITS file class.
Definition: GFits.hpp:63
FITS file class interface definition.
bool is_empty(void) const
Signals if there are no ephemerides for pulsar.
Definition: GPulsar.hpp:164
const std::string & timesys(void) const
Returns pulsar ephemeris time system.
void append(const GTime &tstart, const GTime &tstop)
Append Good Time Interval.
Definition: GGti.cpp:269
Good time interval class interface definition.
virtual std::string string(const int &row, const int &inx=0) const =0
const GPulsarEphemeris & ephemeris(const GTime &time) const
Return pulsar ephemeris.
Definition: GPulsar.cpp:253
std::string m_name
Pulsar name.
Definition: GPulsar.hpp:97
const double sec_in_day
Definition: GTools.hpp:49
double real(const std::string &keyname) const
Return card value as double precision.
Definition: GFitsHDU.hpp:423
Ephemerides class definition.
const GTime & tstop(void) const
Returns validity stop time.
Filename class.
Definition: GFilename.hpp:62
GPulsar(void)
Void constructor.
Definition: GPulsar.cpp:65
#define G_LOAD_INTEGRAL
Definition: GPulsar.cpp:43
Abstract interface for FITS table column.
double geo2ssb(const GSkyDir &srcdir, const GTime &time) const
Get time difference between geocentric and SSB (seconds)
virtual std::string print(const GChatter &chatter=NORMAL) const
Print Pulsar.
Definition: GPulsar.cpp:381
void load_psrtime(const GFilename &filename, const std::string &name="")
Load Pulsar from ephemerides psrtime file.
Definition: GPulsar.cpp:740
Ephemerides class.
GPulsarEphemeris & at(const int &index)
Return reference to ephemeris.
Definition: GPulsar.cpp:204
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
GChatter
Definition: GTypemaps.hpp:33
#define G_AT
Definition: GPulsar.cpp:39
void load_integral(const GFitsTable *table, const std::string &name="")
Load Pulsar from INTEGRAL ephemerides FITS table.
Definition: GPulsar.cpp:529
double f1(void) const
Returns pulsar frequency derivative (s^-2)
Good Time Interval class.
Definition: GGti.hpp:62
bool is_fits(void) const
Checks whether file is a FITS file.
Definition: GFilename.cpp:313
#define G_LOAD
Definition: GPulsar.cpp:41
void radec_deg(const double &ra, const double &dec)
Set equatorial sky direction (degrees)
Definition: GSkyDir.cpp:224
const GTime & tstop(void) const
Returns latest stop time in Good Time Intervals.
Definition: GGti.hpp:207
#define G_LOAD_FITS
Definition: GPulsar.cpp:42
const GTime & tstart(void) const
Returns earliest start time in Good Time Intervals.
Definition: GGti.hpp:194
void load_fermi(const GFitsTable *table, const std::string &name="")
Load Pulsar from Fermi ephemerides FITS table.
Definition: GPulsar.cpp:632
Pulsar ephemeris class.
void init_members(void)
Initialise class members.
Definition: GPulsar.cpp:434
Pulsar class.
Definition: GPulsar.hpp:53
std::string rstrip_chars(const std::string &arg, const std::string &chars)
Strip trailing character from string.
Definition: GTools.cpp:131
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
virtual int integer(const int &row, const int &inx=0) const =0
double f0(void) const
Returns pulsar frequency (Hz)
void load_parfile(const GFilename &filename)
Load Pulsar from ephemeris par file.
Definition: GPulsar.cpp:885
GGti validity(void) const
Return validity intervals of pulsar ephemerides.
Definition: GPulsar.cpp:286
virtual double real(const int &row, const int &inx=0) const =0
int size(void) const
Return number of ephemerides for pulsar.
Definition: GPulsar.hpp:150
std::string string(const std::string &keyname) const
Return card value as string.
Definition: GFitsHDU.hpp:410
Pulsar class definition.
double mjd(void) const
Return time in Modified Julian Days (TT)
Definition: GTime.cpp:320
const std::string & name(void) const
Return pulsar name.
Definition: GPulsar.hpp:176
virtual void clear(void)
Clear Pulsar.
Definition: GPulsar.cpp:171
#define G_EPHEMERIS
Definition: GPulsar.cpp:40
virtual GPulsar * clone(void) const
Clone Pulsar.
Definition: GPulsar.cpp:189
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
GPulsar & operator=(const GPulsar &pulsar)
Assignment operator.
Definition: GPulsar.cpp:141
const GTime & tstart(void) const
Returns validity start time.
Sky direction class.
Definition: GSkyDir.hpp:62
std::string utc(const int &precision=0) const
Return time as string in UTC time system.
Definition: GTime.cpp:465
double utc2tt(void) const
Return time difference between UTC and TT (seconds)
Definition: GTime.hpp:197
Filename class interface definition.
double todouble(const std::string &arg)
Convert string into double precision value.
Definition: GTools.cpp:926
std::vector< GPulsarEphemeris > m_ephemerides
Pulsar ephemerides.
Definition: GPulsar.hpp:98
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
double phase(void) const
Returns pulse phase.