Commit 6449c54ed6a1353b79ec3b21bfa3d4ddd12c9c1d

Authored by Alain Klotz
1 parent f2387923
Exists in master

Update table management and add Filename rule PyROS.eph.1

src/guitastro/astrotables.py
... ... @@ -65,8 +65,9 @@ class AstroTableException(GuitastroException):
65 65 NO_OBJECT_FOUND = 6
66 66 ERROR_NOT_REFERENCED = 7
67 67 COLNAME_NOT_FOUND = 8
  68 + TOO_MANY_DIMENSIONS = 9
68 69  
69   - errors = [""]*9
  70 + errors = [""]*10
70 71 errors[TABLE_EMPTY] = "Table is empty"
71 72 errors[BAD_NLIG] = "Bad number of rows"
72 73 errors[BAD_IDXLIG] = "Bad index of row"
... ... @@ -76,6 +77,7 @@ class AstroTableException(GuitastroException):
76 77 errors[NO_OBJECT_FOUND] = "No object found"
77 78 errors[ERROR_NOT_REFERENCED] = "Error not referenced"
78 79 errors[COLNAME_NOT_FOUND] = "A column name was not found"
  80 + errors[TOO_MANY_DIMENSIONS] = "Too many dimensions"
79 81  
80 82  
81 83 class AstroTable(AstroTableException, GuitastroTools):
... ... @@ -183,6 +185,14 @@ class AstroTable(AstroTableException, GuitastroTools):
183 185  
184 186 at.t.write.list_formats()
185 187  
  188 + It is possible to use the file context of the class Filenames to manage the write() and read() methods.
  189 +
  190 + ::
  191 +
  192 + at.useFileNames = True
  193 + at.fn.rootdir = "/tmp"
  194 + at.t.write('my_file_name', overwrite=True)
  195 +
186 196 """
187 197  
188 198 VERBOSE_NONE = 0
... ... @@ -193,7 +203,10 @@ class AstroTable(AstroTableException, GuitastroTools):
193 203  
194 204 INPUT_UNKNOWN = -1
195 205 INPUT_DEFAULT = 0
196   - INPUT_REGULAR = 1
  206 + INPUT_LISTOFLIST = 1
  207 + INPUT_ASTROPYTABLE = 2
  208 + INPUT_ASTROPYQTABLE = 3
  209 + INPUT_NUMPYARRAY = 4
197 210  
198 211 def __init__(self, *args: str, **kwargs: int):
199 212 self.t = None
... ... @@ -337,14 +350,18 @@ class AstroTable(AstroTableException, GuitastroTools):
337 350 """
338 351 return list(map(list, zip(*listoflist)))
339 352  
340   - def table(self, arr, *args: str, **kwargs: int):
341   - """Create a new table
  353 + def table(self, inp: Any, *args: str, **kwargs: int) -> None:
  354 + """Create a new table from a List of List or from an Astropy Table
  355 +
  356 + The internal management is based on astropy QTable.
342 357  
343   - The internal management is based on astropy Table.
  358 + Args:
  359 + inp: List of List or an Astropy Table
  360 + **kwds: Options of QTable as defined in https://docs.astropy.org/en/stable/api/astropy.table.QTable.html
344 361  
345 362 Example:
346 363  
347   - For tests, a default table can be created passing None as arr:
  364 + The input is a List of List and the option 'names' allow to define the column names:
348 365  
349 366 ::
350 367  
... ... @@ -398,30 +415,38 @@ class AstroTable(AstroTableException, GuitastroTools):
398 415 This default table contains also metadata.
399 416 See methods getkwds() and setkwd() to manage the keys of metadata.
400 417 """
  418 + # --- Identify the input type
401 419 self._input = self.INPUT_UNKNOWN
402   - na = len(args)
403   - if arr is None:
404   - if na == 0:
405   - self._input = self.INPUT_DEFAULT
406   - else:
407   - if na == 0:
408   - self._input = self.INPUT_REGULAR
409   - else:
410   - inp = args[0].upper()
411   - if inp == "REGULAR":
412   - self._input = self.INPUT_REGULAR
413   - if inp == "SEP":
414   - self._input = self.INPUT_SEP
415   - # ===
  420 + if repr(type(inp)) == "<class 'astropy.table.table.Table'>":
  421 + self._input = self.INPUT_ASTROPYTABLE
  422 + if repr(type(inp)) == "<class 'astropy.table.table.QTable'>":
  423 + self._input = self.INPUT_ASTROPYQTABLE
  424 + if self._input == self.INPUT_UNKNOWN:
  425 + if isinstance(inp, List):
  426 + self._input = self.INPUT_LISTOFLIST
  427 + if repr(inp) == "<class 'numpy.ndarray'>":
  428 + if len(inp.shape) >2:
  429 + msg = f"Input array has {len(inp.shape)} dimensions but must have only 1 or 2."
  430 + raise AstroTableException(AstroTableException.TOO_MANY_DIMENSIONS, msg)
  431 + self._input = self.INPUT_NUMPYARRAY
  432 + if self._input == self.INPUT_UNKNOWN:
  433 + self._input = self.INPUT_DEFAULT
  434 + # --- Assign table contents
416 435 if self._input == self.INPUT_DEFAULT:
417 436 x = np.linspace(0, 3, 4)
418 437 y = x*x
419   - arr = [x, y]
  438 + inp = [x, y]
420 439 kwargs['names'] = ('x', 'y')
421 440 kwargs['meta'] = {'DATE-OBS': '2020-02-22T02:20:22', 'AUTHOR': 'Guitastro'}
422   - self.t = QTable(arr, **kwargs)
423   - elif self._input == self.INPUT_REGULAR:
424   - self.t = QTable(arr, **kwargs)
  441 + self.t = QTable(inp, **kwargs)
  442 + elif self._input == self.INPUT_LISTOFLIST:
  443 + self.t = QTable(inp, **kwargs)
  444 + elif self._input == self.INPUT_ASTROPYTABLE:
  445 + self.t = QTable(inp, **kwargs)
  446 + elif self._input == self.INPUT_ASTROPYQTABLE:
  447 + self.t = QTable(inp, **kwargs)
  448 + elif self._input == self.INPUT_NUMPYARRAY:
  449 + self.t = QTable(inp, **kwargs)
425 450  
426 451 # =============================================
427 452 # Manage table columns
... ... @@ -449,9 +474,16 @@ class AstroTable(AstroTableException, GuitastroTools):
449 474 self._verify_table()
450 475 return self.t.keys()
451 476  
452   - def getcol(self, colname: str, unit: bool=False):
  477 + def getcol(self, colname: str, unit: bool=False) -> any:
453 478 """Return the values of a column of a given name
454 479  
  480 + Args:
  481 + colname: Name of the column
  482 + unit: In case of a column with a unit, set True to keep the original unit format. False to return float values.
  483 +
  484 + Returns:
  485 + the column contents formated as specified by 'unit'.
  486 +
455 487 Example:
456 488  
457 489 ::
... ... @@ -481,8 +513,17 @@ class AstroTable(AstroTableException, GuitastroTools):
481 513 def setcol(self, colname: str, colvalues: Any, **kwargs):
482 514 """Add or modify a column and its values
483 515  
  516 + Args:
  517 + colname: Name of the column
  518 + colvalues: Contents of the column
  519 + **kwargs:
  520 +
  521 + * index: The index where the new column must be added.
  522 +
484 523 Example:
485 524  
  525 + At a column values at the column index = 0:
  526 +
486 527 ::
487 528  
488 529 at = AstroTable()
... ... @@ -921,9 +962,18 @@ class AstroTable(AstroTableException, GuitastroTools):
921 962 nrow, ncol = self.shape()
922 963 self.t.remove_rows(indexes)
923 964  
924   - def keepvalidrows(self, colnames: list):
  965 + def keepvalidrows(self, colnames: list) -> List:
925 966 """Keep only rows where values of colnames are valids.
926 967  
  968 + After this operation the table is modified.
  969 +
  970 + Args:
  971 + colnames: List of colnames to check. If colnames=[] all columns will be checked.
  972 +
  973 + Returns:
  974 + List of bad row indexes found in the input table.
  975 +
  976 +
927 977 Example:
928 978  
929 979 To delete rows from an index to another one:
... ... @@ -959,28 +1009,34 @@ class AstroTable(AstroTableException, GuitastroTools):
959 1009 self._verify_table()
960 1010 nrow, ncol = self.shape()
961 1011 bad_indexes = set()
  1012 + if len(colnames)==0:
  1013 + colnames = self.getcols()
962 1014 for colname in colnames:
963 1015 col = self.getcol(colname, False)
964   - print(f"{colname=} {str(type(col))=} {col=}")
  1016 + #print(f"{colname=} {str(type(col))=} {col=}")
965 1017 if str(type(col)) == "<class 'astropy.utils.masked.core.MaskedNDArray'>":
966 1018 idxs = set(np.where(col.mask == True)[0])
967   - print(f"{colname=} {idxs=}")
  1019 + #print(f"{colname=} {idxs=}")
968 1020 bad_indexes.update(idxs)
969 1021 if str(type(col)) == "<class 'numpy.ndarray'>":
970 1022 idxs = set(np.where(np.isnan(col))[0])
971   - print(f"{colname=} {idxs=}")
  1023 + #print(f"{colname=} {idxs=}")
972 1024 bad_indexes.update(idxs)
973 1025 bad_indexes = list(bad_indexes)
974   - print(f"{bad_indexes=}")
  1026 + #print(f"{bad_indexes=}")
975 1027 self.t.remove_rows(bad_indexes)
  1028 + return bad_indexes
976 1029  
977 1030 # =============================================
978 1031 # Manage table metadata
979 1032 # =============================================
980 1033  
981   - def getkwds(self):
  1034 + def getkwds(self)->List:
982 1035 """Return the list of keys of metadata of the table
983 1036  
  1037 + Return:
  1038 + The list of all column names (keys).
  1039 +
984 1040 Example:
985 1041  
986 1042 ::
... ... @@ -1426,6 +1482,8 @@ class AstroTable(AstroTableException, GuitastroTools):
1426 1482 Distances can be computed as angular or cartesians according the type of the columns.
1427 1483 Anyway one can force the type by the key 'angular' of \**kwargs.
1428 1484  
  1485 + The output of this method is an input of the method merge().
  1486 +
1429 1487 Args:
1430 1488 astrotable: The AstroTable to match with the current one
1431 1489 colname1: Column name of the first coordinate component ('x', 'ra', etc.)
... ... @@ -1436,7 +1494,7 @@ class AstroTable(AstroTableException, GuitastroTools):
1436 1494 * angular (bool): Force type of computation (angular or cartesian). the Defaut is False meaning that is no units are found in the column description, the cartesian computation will be done.
1437 1495  
1438 1496 Returns:
1439   - A list of couples of row indexes of the current AstroTable and the AstroTable passed as parameter.
  1497 + A list of couples of row indexes of the current AstroTable and the AstroTable passed as parameter. This is interresting as input of the method merge().
1440 1498  
1441 1499 This method can be very slow in case of AstroTables having more than 1000 rows.
1442 1500  
... ...
src/guitastro/ephemeris.py
... ... @@ -46,6 +46,11 @@ except:
46 46 from filenames import FileNames
47 47  
48 48 try:
  49 + from .astrotables import AstroTable
  50 +except:
  51 + from astrotables import AstroTable
  52 +
  53 +try:
49 54 from .guitastrotools import GuitastroTools, GuitastroException
50 55 except:
51 56 from guitastrotools import GuitastroTools, GuitastroException
... ... @@ -582,16 +587,16 @@ class Ephemeris(EphemerisException, GuitastroTools):
582 587 location = ( f"{self.home.longitude}d", f"{self.home.latitude}d", f"{self.home.altitude:.1f}m")
583 588 try:
584 589 table = MPC.get_ephemeris(target, location=location, number=1, start=start)
  590 + found = True
585 591 except:
586   - table = None
587   - if table != None:
  592 + pass
  593 + if found == True:
588 594 colnames = table.colnames
589 595 data= table.as_array()[0]
590 596 index = colnames.index('RA')
591 597 ra = data[index]
592 598 index = colnames.index('Dec')
593 599 dec = data[index]
594   - found = True
595 600 # ---
596 601 if target_type == self.TARGET_TYPE_RADEC:
597 602 reference = "Manual entry RA,Dec (astropy.coordinates.SkyCoord)"
... ... @@ -718,18 +723,21 @@ class Ephemeris(EphemerisException, GuitastroTools):
718 723 #print("==> Try Simbad")
719 724 try:
720 725 table = Simbad.query_object(target)
  726 + found = True
721 727 except:
722 728 pass
723   - if table != None:
724   - colnames = table.colnames
725   - data= table.as_array()[0]
726   - index = colnames.index('RA')
727   - ra = data[index]
728   - index = colnames.index('DEC')
729   - dec = data[index]
730   - ra = Angle(ra).deg()*15
731   - dec = Angle(dec).deg()
732   - found = True
  729 + if found == True:
  730 + try:
  731 + colnames = table.colnames
  732 + data= table.as_array()[0]
  733 + index = colnames.index('RA')
  734 + ra = data[index]
  735 + index = colnames.index('DEC')
  736 + dec = data[index]
  737 + ra = Angle(ra).deg()*15
  738 + dec = Angle(dec).deg()
  739 + except:
  740 + found = False
733 741 # --- Try a MPC object
734 742 if found == False:
735 743 reference = "Minor Planet Center (astroquery.mpc)"
... ... @@ -738,16 +746,17 @@ class Ephemeris(EphemerisException, GuitastroTools):
738 746 location = ( f"{self.home.longitude}d", f"{self.home.latitude}d", f"{self.home.altitude:.1f}m")
739 747 try:
740 748 table = MPC.get_ephemeris(target, location=location, number=1, start=start)
  749 + found = True
741 750 except:
742   - table = None
743   - if table != None:
  751 + pass
  752 + return table
  753 + if found == True:
744 754 colnames = table.colnames
745 755 data= table.as_array()[0]
746 756 index = colnames.index('RA')
747 757 ra = data[index]
748 758 index = colnames.index('Dec')
749 759 dec = data[index]
750   - found = True
751 760 # ---
752 761 if found == False:
753 762 msg = f"The target {target} was not found in Skyfield, Simbad and MPC databases"
... ... @@ -1489,7 +1498,19 @@ class Ephemeris(EphemerisException, GuitastroTools):
1489 1498 eph['ddec_equinox'] = ddecequinoxs
1490 1499 return eph
1491 1500  
1492   - def hadec2ephem(self, ha, dec, date, **kwargs):
  1501 + def hadec2ephem(self, ha:float, dec:float, date:Date, **kwargs)->dict:
  1502 + """Compute local celestial coordinates (alt, az) and catalog coordinates (ra_equinox, dec_equinox) from a local (ha, dec)
  1503 +
  1504 + The Earth location of the observatory must be defined using set_home() before calling this method.
  1505 +
  1506 + Args:
  1507 + ha: Hour angle
  1508 + dec: True declination
  1509 +
  1510 + Returns:
  1511 + A ephemeris dictionary with keys (ha, dec, alt, az, ra_equinox, dec_equinox)
  1512 +
  1513 + """
1493 1514 if 'humidity' in kwargs.keys():
1494 1515 rel_humidity = kwargs['humidity']
1495 1516 else:
... ... @@ -1534,7 +1555,19 @@ class Ephemeris(EphemerisException, GuitastroTools):
1534 1555 ephem['alt'] = alt
1535 1556 return ephem
1536 1557  
1537   - def altaz2ephem(self, az, alt, date, **kwargs):
  1558 + def altaz2ephem(self, az: float, alt: float, date: Date, **kwargs) -> dict:
  1559 + """Compute local celestial coordinates (ha, dec) and catalog coordinates (ra_equinox, dec_equinox) from a local (ha, dec)
  1560 +
  1561 + The Earth location of the observatory must be defined using set_home() before calling this method.
  1562 +
  1563 + Args:
  1564 + az: Azimuth
  1565 + alt: Elevation
  1566 +
  1567 + Returns:
  1568 + A ephemeris dictionary with keys (ha, dec, alt, az, ra_equinox, dec_equinox)
  1569 +
  1570 + """
1538 1571 if 'humidity' in kwargs.keys():
1539 1572 rel_humidity = kwargs['humidity']
1540 1573 else:
... ... @@ -1667,7 +1700,7 @@ if __name__ == &quot;__main__&quot;:
1667 1700 """
1668 1701 eph = Ephemeris()
1669 1702 eph.set_home("guitalens")
1670   - name = "2022 AB"
  1703 + name = "C/2023 P1" #"2022 AB" "C/2023 P1"
1671 1704 ephem = eph.radec(name, date="now")
1672 1705 print(f"{name} ra={ephem['ra_equinox']} dec={ephem['dec_equinox']} equinox={ephem['header']['equinox']} epoch={ephem['jd']}")
1673 1706  
... ... @@ -1950,7 +1983,7 @@ if __name__ == &quot;__main__&quot;:
1950 1983 Load a matrix file generated by PyROS
1951 1984 """
1952 1985 eph = Ephemeris()
1953   - fname = r"c:\srv\develop\pyros_soft\pyros\data\sequence\P001\20230721\scheduler_schedule.txt"
  1986 + fname = r"C:\srv\develop\pyros_soft\pyros\data\sequence\P001\20230831\scheduler_schedule.txt"
1954 1987 eph.skdl_plot_schedule(fname)
1955 1988  
1956 1989  
... ...
src/guitastro/filenames.py
... ... @@ -290,10 +290,11 @@ class FileNames(FileNamesException, GuitastroTools):
290 290  
291 291 NAMING_NONE = 0
292 292 NAMING_PYROS_IMG1 = 1
293   - NAMING_PYROS_SEQ1 = 2
294   - NAMING_ROS1 = 3
295   - NAMING_T1M1 = 4
296   - _naming_list = ["", "PyROS.img.1", "PyROS.seq.1", "ROS.1", "T1M.1"]
  293 + NAMING_PYROS_EPH1 = 2
  294 + NAMING_PYROS_SEQ1 = 3
  295 + NAMING_ROS1 = 4
  296 + NAMING_T1M1 = 5
  297 + _naming_list = ["", "PyROS.img.1", "PyROS.eph.1", "PyROS.seq.1", "ROS.1", "T1M.1"]
297 298 _naming_0_stypes = ["C0A", "L0A", "L1A", "L1B", "L1C", "L1D", "L1E", "L2A", "DA0", "DA1", "FL0", "FL1", "BI0", "BI1"]
298 299 _naming_1_stypes = ["AL", "IM", "BI", "DA", "FL"]
299 300 _see_naming_rules = ". See method naming_rules() to get rules"
... ... @@ -488,6 +489,33 @@ class FileNames(FileNamesException, GuitastroTools):
488 489 texte += "CCC is a Channel for L0A, L1A, L1B, L1C ftype levels but should be the Album for L1D level"
489 490 return texte
490 491  
  492 + def __naming_rules_pyros_eph1(self, section):
  493 + name = self._naming_list[self.fcontext_value('naming')]
  494 + texte = ""
  495 + if section == self._NAMING_RULES_SECTION_DATE:
  496 + texte += "2023-09-09T08:24:13"
  497 + elif section == self._NAMING_RULES_SECTION_INTRO:
  498 + texte +=f"{name} is a file name format for the ephemeris of Python Robotic Observatory Software.\n"
  499 + texte += "Specific terminology:\n"
  500 + texte += "* Period: Symbol that defines a range of dates.\n"
  501 + texte += "* Unit: The set of Mount and Channels that define a 'telescope'.\n"
  502 + elif section == self._NAMING_RULES_SECTION_FORMAT:
  503 + texte += " 1 2 3 4 5\n"
  504 + texte += "0123456789 123456789 123456789 123456789 123456789 123\n"
  505 + texte += "PPPP_YYYYMMDD_V_UUU_t*\n"
  506 + texte += "\n"
  507 + texte += "PPPP = 'period' = Period of validity\n"
  508 + texte += "YYYYMMDD = 'date' = Year Month Day. e.g. 20221109\n"
  509 + texte += "V = 'version' = Version of the naming rule. e.g. 1\n"
  510 + texte += "UUU = 'unit' = Telescope unit. e.g. TNC\n"
  511 + texte += "t* = Celestial target. e.g. sun\n"
  512 + elif section == self._NAMING_RULES_SECTION_REMARKS:
  513 + texte += "Example: P001_20221109_1_TNC_sun\n"
  514 + texte += "UUU, PPPP are accronyms (string)\n"
  515 + texte += "V, is digits (int)\n"
  516 + texte += "t*, is a celestial name (string)\n"
  517 + return texte
  518 +
491 519 def __naming_rules_pyros_seq1(self, section):
492 520 name = self._naming_list[self.fcontext_value('naming')]
493 521 texte = ""
... ... @@ -501,8 +529,6 @@ class FileNames(FileNamesException, GuitastroTools):
501 529 texte += "* Channel: An optical path + camera of a Unit. A unit can have many Channels.\n"
502 530 texte += "* Album: A set of Channels for which data can be combined by post processing.\n"
503 531 texte += "* Sequence: A set of Albums. A sequence is a hierarchy Channels/Planes/Frames.\n"
504   - texte += "* Plane: A set of Frames. A Plane is composed by Frames.\n"
505   - texte += "* Frame: A unitary record (e.g. an image).\n"
506 532 elif section == self._NAMING_RULES_SECTION_FORMAT:
507 533 texte += " 1 2 3 4 5\n"
508 534 texte += "0123456789 123456789 123456789 123456789 123456789 123\n"
... ... @@ -512,7 +538,7 @@ class FileNames(FileNamesException, GuitastroTools):
512 538 texte += "YYYYMMDD = 'date' = Year Month Day. e.g. 20221109\n"
513 539 texte += "V = 'version' = Version of the naming rule. e.g. 1\n"
514 540 texte += "UUU = 'unit' = Telescope unit. e.g. TNC\n"
515   - texte += "IIIIIIIIII = 'id_seq' = ID of the file in the database. e.g. 0000001234\n"
  541 + texte += "IIIIIIIIII = 'id_seq' = ID of the sequence in the database. e.g. 0000001234\n"
516 542 elif section == self._NAMING_RULES_SECTION_REMARKS:
517 543 texte += "Example: P001_20221109_1_TNC_0123456789\n"
518 544 texte += "UUU, PPPP are accronyms (string)\n"
... ... @@ -617,6 +643,8 @@ class FileNames(FileNamesException, GuitastroTools):
617 643 return texte
618 644 elif self.fcontext_value('naming') == self.NAMING_PYROS_IMG1:
619 645 texte += self.__naming_rules_pyros_img1(self._NAMING_RULES_SECTION_INTRO)
  646 + elif self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  647 + texte += self.__naming_rules_pyros_eph1(self._NAMING_RULES_SECTION_INTRO)
620 648 elif self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
621 649 texte += self.__naming_rules_pyros_seq1(self._NAMING_RULES_SECTION_INTRO)
622 650 elif self.fcontext_value('naming') == self.NAMING_ROS1:
... ... @@ -628,6 +656,8 @@ class FileNames(FileNamesException, GuitastroTools):
628 656 texte +=f"\n{h1}\n" + "="*len(h1) + "\n\n"
629 657 if self.fcontext_value('naming') == self.NAMING_PYROS_IMG1:
630 658 texte += self.__naming_rules_pyros_img1(self._NAMING_RULES_SECTION_FORMAT)
  659 + elif self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  660 + texte += self.__naming_rules_pyros_eph1(self._NAMING_RULES_SECTION_FORMAT)
631 661 elif self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
632 662 texte += self.__naming_rules_pyros_seq1(self._NAMING_RULES_SECTION_FORMAT)
633 663 elif self.fcontext_value('naming') == self.NAMING_ROS1:
... ... @@ -639,6 +669,8 @@ class FileNames(FileNamesException, GuitastroTools):
639 669 texte +=f"\n{h1}\n" + "="*len(h1) + "\n\n"
640 670 if self.fcontext_value('naming') == self.NAMING_PYROS_IMG1:
641 671 texte += self.__naming_rules_pyros_img1(self._NAMING_RULES_SECTION_REMARKS)
  672 + elif self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  673 + texte += self.__naming_rules_pyros_eph1(self._NAMING_RULES_SECTION_REMARKS)
642 674 elif self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
643 675 texte += self.__naming_rules_pyros_seq1(self._NAMING_RULES_SECTION_REMARKS)
644 676 elif self.fcontext_value('naming') == self.NAMING_ROS1:
... ... @@ -745,6 +777,7 @@ class FileNames(FileNamesException, GuitastroTools):
745 777 pass
746 778 self.fcontext_value('naming', old_naming)
747 779 if naming == self.NAMING_NONE and n>3:
  780 + # case of floating length file names
748 781 if fname[:3] == "T1M":
749 782 fname += suffixes[0]
750 783 param = {}
... ... @@ -776,6 +809,35 @@ class FileNames(FileNamesException, GuitastroTools):
776 809 except:
777 810 pass
778 811 self.fcontext_value('naming', old_naming)
  812 + # --- Try NAMING_PYROS_EPH1
  813 + words = fname.split("_")
  814 + n = len(words)
  815 + if n >= 5:
  816 + valid = False
  817 + if len(words[0])==4 and len(words[1])==8 and len(words[2])==1 and len(words[3])==3:
  818 + valid = True
  819 + if valid == True:
  820 + if words[0][0] != 'P':
  821 + valid == False
  822 + if valid == True:
  823 + param = {}
  824 + param['period'] = words[0]
  825 + param['date'] = words[1]
  826 + param['version'] = words[2]
  827 + param['unit'] = words[3]
  828 + k4 = 0
  829 + for k in range(4):
  830 + k4 += len(words[k]) + 1
  831 + param['target'] = fname[k4:]
  832 + old_naming = self.fcontext_value('naming')
  833 + self.fcontext_value('naming', self.NAMING_PYROS_EPH1)
  834 + try:
  835 + fname_verified = self.naming_set(**param)
  836 + if fname == fname_verified:
  837 + naming = self.NAMING_PYROS_EPH1
  838 + except:
  839 + pass
  840 + self.fcontext_value('naming', old_naming)
779 841 return self._naming_list[naming]
780 842  
781 843 def naming_get(self, fname:str) -> dict:
... ... @@ -829,6 +891,29 @@ class FileNames(FileNamesException, GuitastroTools):
829 891 fname_verified = self.naming_set(**param)
830 892 if fname != fname_verified:
831 893 raise FileNamesException(FileNamesException.BAD_FILENAME_RULE)
  894 + elif self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  895 + n = len(fname)
  896 + nn = 20
  897 + if n <= nn:
  898 + texte =f"File name '{fname}' must be a string of at less {nn} characters" + see_rules
  899 + raise FileNamesException(FileNamesException.BAD_FILENAME_RULE, texte)
  900 + words = fname.split("_")
  901 + n = len(words)
  902 + nn = 5
  903 + if n < nn:
  904 + texte =f"File name '{fname}' must contain at less {nn-1} underscore characters" + see_rules
  905 + raise FileNamesException(FileNamesException.BAD_FILENAME_RULE, texte)
  906 + param['period'] = words[0]
  907 + param['date'] = words[1]
  908 + param['version'] = words[2]
  909 + param['unit'] = words[3]
  910 + k4 = 0
  911 + for k in range(4):
  912 + k4 += len(words[k]) + 1
  913 + param['target'] = fname[k4:]
  914 + fname_verified = self.naming_set(**param)
  915 + if fname != fname_verified:
  916 + raise FileNamesException(FileNamesException.BAD_FILENAME_RULE)
832 917 elif self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
833 918 n = len(fname)
834 919 nn = 30
... ... @@ -942,6 +1027,8 @@ class FileNames(FileNamesException, GuitastroTools):
942 1027 wildcard = "*"
943 1028 elif self.fcontext_value('naming') == self.NAMING_PYROS_IMG1:
944 1029 wildcard = "???_????????_????????????_?_???_???_??????????_???_???"
  1030 + elif self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  1031 + wildcard = "????_????????_?_???_*"
945 1032 elif self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
946 1033 wildcard = "????_????????_?_???_??????????"
947 1034 elif self.fcontext_value('naming') == self.NAMING_ROS1:
... ... @@ -1095,6 +1182,100 @@ class FileNames(FileNamesException, GuitastroTools):
1095 1182 raise FileNamesException(FileNamesException.BAD_PARAM, texte)
1096 1183 # --- form final file name
1097 1184 fname = param['ftype']+"_"+param['date']+"_"+param['time']+"_"+param['version']+"_"+param['unit']+"_"+param['channel']+"_"+param['id_seq']+"_"+param['plane']+"_"+param['frame']
  1185 + if self.fcontext_value('naming') == self.NAMING_PYROS_EPH1:
  1186 + if na > 0:
  1187 + # case the dict is not **kwargs but *args
  1188 + if isinstance(args[0], dict):
  1189 + kwargs = args[0]
  1190 + nk = len(kwargs)
  1191 + else:
  1192 + texte = "Dictionary of input parameters not found " + see_rules
  1193 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1194 + elif nk == 0:
  1195 + return wildcard
  1196 + # -- read kwargs
  1197 + wildcards = wildcard.split("_")
  1198 + param = {}
  1199 + param['period'] = wildcards[0]
  1200 + param['date'] = wildcards[1]
  1201 + param['version'] = wildcards[2]
  1202 + param['unit'] = wildcards[3]
  1203 + param['target'] = wildcards[4]
  1204 + if nk > 0:
  1205 + for key, val in kwargs.items():
  1206 + if key in param.keys():
  1207 + param[key] = val
  1208 + # --- verify all params are set
  1209 + for key, val in param.items():
  1210 + if val == "":
  1211 + texte = f"param {key} must not be empty string" + see_rules
  1212 + raise FileNamesException(FileNamesException.EMPTY_STRING, texte)
  1213 + # --- verify period
  1214 + valid = False
  1215 + if isinstance(param['period'],str):
  1216 + if param['period'] == wildcards[0]:
  1217 + valid = True
  1218 + elif len(param['period']) == 4:
  1219 + valid = True
  1220 + if valid == False:
  1221 + texte = f"Period {param['period']} must be a string of 4 digits" + see_rules
  1222 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1223 + # --- verify date
  1224 + valid = False
  1225 + if isinstance(param['date'],str):
  1226 + if param['date'] == wildcards[1]:
  1227 + valid = True
  1228 + elif len(param['date']) == 8:
  1229 + if param['date'].isdigit():
  1230 + valid = True
  1231 + elif param['date'] == "*" or param['date'] == "now":
  1232 + d = datetime.datetime.utcnow().isoformat()
  1233 + fdate = d[0:4]+d[5:7]+d[8:10]
  1234 + ftime = d[11:13]+d[14:16]+d[17:19]+d[20:26]
  1235 + param['date'] = fdate
  1236 + param['time'] = ftime
  1237 + valid = True
  1238 + if valid == False:
  1239 + texte = f"Date {param['date']} must be a string of 8 digits" + see_rules
  1240 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1241 + # --- verify version
  1242 + valid = False
  1243 + if isinstance(param['version'],str):
  1244 + if param['version'] == wildcards[2]:
  1245 + valid = True
  1246 + elif len(param['version']) == 1:
  1247 + valid = True
  1248 + elif isinstance(param['version'],int):
  1249 + param['version'] = f"{param['version']:01d}"
  1250 + valid = True
  1251 + if valid == False:
  1252 + texte = f"Version {param['version']} must be a string of 1 character" + see_rules
  1253 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1254 + # --- verify unit
  1255 + valid = False
  1256 + if isinstance(param['unit'],str):
  1257 + if param['unit'] == wildcards[3]:
  1258 + valid = True
  1259 + elif len(param['unit']) == 3:
  1260 + valid = True
  1261 + if valid == False:
  1262 + texte = f"Unit {param['unit']} must be a string of 3 characters" + see_rules
  1263 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1264 + # --- verify target
  1265 + valid = False
  1266 + if isinstance(param['target'],str):
  1267 + if param['target'] == wildcards[4]:
  1268 + valid = True
  1269 + if valid == False:
  1270 + v = kwargs.get('target')
  1271 + if v is not None:
  1272 + param['target'] = v
  1273 + valid = True
  1274 + if valid == False:
  1275 + texte = f"Target {param['target']} must be a string" + see_rules
  1276 + raise FileNamesException(FileNamesException.BAD_PARAM, texte)
  1277 + # --- form final file name
  1278 + fname = param['period']+"_"+param['date']+"_"+param['version']+"_"+param['unit']+"_"+param['target']
1098 1279 if self.fcontext_value('naming') == self.NAMING_PYROS_SEQ1:
1099 1280 if na > 0:
1100 1281 # case the dict is not **kwargs but *args
... ... @@ -1881,6 +2062,8 @@ class FileNames(FileNamesException, GuitastroTools):
1881 2062 self.fcontext_value('naming', self.fcontext_value('pathnaming'))
1882 2063 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_IMG1:
1883 2064 self.fcontext_value('pathing', self.PATHING_YYYY_MM_DD)
  2065 + if self.fcontext_value('pathnaming') == self.NAMING_PYROS_EPH1:
  2066 + self.fcontext_value('pathing', self.PATHING_PPPP_YYYYMMDD)
1884 2067 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_SEQ1:
1885 2068 self.fcontext_value('pathing', self.PATHING_PPPP_YYYYMMDD)
1886 2069 return self._naming_list[self.fcontext_value('pathnaming')]
... ... @@ -1924,6 +2107,9 @@ class FileNames(FileNamesException, GuitastroTools):
1924 2107 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_IMG1:
1925 2108 path = self.pathing_set(**pkw)
1926 2109 fname = self.naming_set(**nkw)
  2110 + if self.fcontext_value('pathnaming') == self.NAMING_PYROS_EPH1:
  2111 + path = self.pathing_set(**pkw)
  2112 + fname = self.naming_set(**nkw)
1927 2113 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_SEQ1:
1928 2114 path = self.pathing_set(**pkw)
1929 2115 fname = self.naming_set(**nkw)
... ... @@ -1942,9 +2128,14 @@ class FileNames(FileNamesException, GuitastroTools):
1942 2128 (naming_dict, pathing_dict)
1943 2129  
1944 2130 """
  2131 + pkw = {}
  2132 + nkw = {}
1945 2133 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_IMG1:
1946 2134 pkw = self.pathing_get(filename)
1947 2135 nkw = self.naming_get(filename)
  2136 + if self.fcontext_value('pathnaming') == self.NAMING_PYROS_EPH1:
  2137 + pkw = self.pathing_get(filename)
  2138 + nkw = self.naming_get(filename)
1948 2139 if self.fcontext_value('pathnaming') == self.NAMING_PYROS_SEQ1:
1949 2140 pkw = self.pathing_get(filename)
1950 2141 nkw = self.naming_get(filename)
... ... @@ -3012,8 +3203,8 @@ class FileNames(FileNamesException, GuitastroTools):
3012 3203  
3013 3204 if __name__ == "__main__":
3014 3205  
3015   - default = 17
3016   - example = input(f"Select the example (0 to 17) ({default}) ")
  3206 + default = 18
  3207 + example = input(f"Select the example (0 to 18) ({default}) ")
3017 3208 try:
3018 3209 example = int(example)
3019 3210 except:
... ... @@ -3372,7 +3563,7 @@ if __name__ == &quot;__main__&quot;:
3372 3563  
3373 3564 if example == 17:
3374 3565 """
3375   - Test sequences
  3566 + Test PyROS sequences
3376 3567 """
3377 3568 # --- Create a file context for sequence files
3378 3569 fn.fcontext_create("pyros_seq", "Sequences PyROS")
... ... @@ -3388,5 +3579,26 @@ if __name__ == &quot;__main__&quot;:
3388 3579 param['id_seq'] = 123456789
3389 3580 fname = fn.naming_set(param)
3390 3581 out_fullname = fn.join(fname)
3391   - print(f"\n==== Create an image in the good path (context {fn.fcontext_value('description')}) ====")
  3582 + print(f"\n==== Create a file in the good path (context {fn.fcontext_value('description')}) ====")
  3583 + print(f"{out_fullname=:}")
  3584 +
  3585 + if example == 18:
  3586 + """
  3587 + Test PyROS ephemeris
  3588 + """
  3589 + # --- Create a file context for ephemeris files
  3590 + fn.fcontext_create("pyros_eph", "Ephemeris PyROS")
  3591 + fn.fcontext = "pyros_eph"
  3592 + fn.pathnaming("PyROS.eph.1")
  3593 + fn.rootdir = "/tmp/eph"
  3594 + fn.extension = ".f"
  3595 + param = {}
  3596 + param['period'] = "P001"
  3597 + param['date'] = "20230628"
  3598 + param['unit'] = "TNC"
  3599 + param['version'] = 1
  3600 + param['target'] = "sun_yui"
  3601 + fname = fn.naming_set(param)
  3602 + out_fullname = fn.join(fname)
  3603 + print(f"\n==== Create a file in the good path (context {fn.fcontext_value('description')}) ====")
3392 3604 print(f"{out_fullname=:}")
... ...
src/guitastro/ima.py
... ... @@ -3522,8 +3522,8 @@ class Ima(ImaException, FileNames):
3522 3522  
3523 3523 if __name__ == "__main__":
3524 3524  
3525   - default = 0
3526   - example = input(f"Select the example (0 to 17) ({default}) ")
  3525 + default = 18
  3526 + example = input(f"Select the example (0 to 18) ({default}) ")
3527 3527 try:
3528 3528 example = int(example)
3529 3529 except:
... ... @@ -4074,3 +4074,47 @@ if __name__ == &quot;__main__&quot;:
4074 4074 tkroot.update()
4075 4075 tkroot.mainloop()
4076 4076  
  4077 + if example == 18:
  4078 + ima1 = Ima()
  4079 + ima1.fcontext = "image_in"
  4080 + ima1.rootdir = "c:/d/iut/tp_cours_s5_optoelectronique"
  4081 + fitsname = "image"
  4082 + ima1.load(fitsname)
  4083 + array = ima1._array.flatten()
  4084 + #array = array[0:10000]
  4085 + fmini = np.min(array)
  4086 + fmaxi = np.max(array)
  4087 + bins = np.linspace(fmini, fmaxi, 100)
  4088 + hist, bin_edges = np.histogram(array, bins=bins)
  4089 + bins = (bin_edges[0:-1]+bin_edges[1:])/2
  4090 + plt.semilogy(bins, hist, 'bo-', color="#FF0000")
  4091 + plt.xlabel("Pixel value (ADU)")
  4092 + plt.ylabel("Number of pixels")
  4093 + #plt.xlim([0., 10000.])
  4094 + plt.show()
  4095 +
  4096 + binbits = np.linspace(0,15,16, dtype=int)
  4097 + histbits = np.ones(16, dtype=int)
  4098 + npix = len(array)
  4099 + for kpix in range(npix):
  4100 + xbin = bin(array[kpix])[2:]
  4101 + nb = 16-len(xbin)
  4102 + if nb>0:
  4103 + xbin = "0"*nb + xbin
  4104 + xbin = xbin[::-1]
  4105 + for kbit in range(16):
  4106 + if xbin[kbit] == '1':
  4107 + histbits[kbit] += 1
  4108 + plt.plot(binbits[0:4], histbits[0:4], 'bo-', color="#FF0000")
  4109 + plt.plot(binbits[4:8], histbits[4:8], 'bo-', color="#0000FF")
  4110 + plt.plot(binbits[8:12], histbits[8:12], 'bo-', color="#FF0000")
  4111 + plt.plot(binbits[12:16], histbits[12:16], 'bo-', color="#0000FF")
  4112 + plt.xlabel("Bit")
  4113 + plt.ylabel("Number of occurences bit=1")
  4114 + plt.show()
  4115 +
  4116 + ima1.visu()
  4117 +
  4118 +
  4119 +
  4120 +
... ...