Commit 6449c54ed6a1353b79ec3b21bfa3d4ddd12c9c1d
1 parent
f2387923
Exists in
master
Update table management and add Filename rule PyROS.eph.1
Showing
4 changed files
with
412 additions
and
65 deletions
Show diff stats
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__ == "__main__": |
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__ == "__main__": |
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__ == "__main__": |
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__ == "__main__": |
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__ == "__main__": |
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 | + | |
... | ... |