Commit 519258d7d2919e97330d71ddf89ca9c59b73e94e
1 parent
7a80ffac
Exists in
master
Add doc for components and devices.
Showing
9 changed files
with
406 additions
and
69 deletions
Show diff stats
... | ... | @@ -0,0 +1,78 @@ |
1 | +*********************************************** | |
2 | +Device management with guitastro Python classes | |
3 | +*********************************************** | |
4 | + | |
5 | +This section explains how to use guitastro to drive a device. | |
6 | + | |
7 | +1. Context | |
8 | +********** | |
9 | + | |
10 | +A device is a system constituted by: | |
11 | + | |
12 | + * **communication**: Communication means that messages to drive the device are exchanged between guitastro and the device. Messages can be transported by Serial or TCP protocols. | |
13 | + * **components**: A component is a part of the device identified to realize an astronomical action. For example a focuser is a component. Some devices are constituted by many compenents. For example a camera équiped by a shutter and a filter wheel is a device constituted by 3 components. | |
14 | + | |
15 | +In Guitastro we define the following **categories** of components: | |
16 | + | |
17 | + * **MountPointing**: It is a mechanical motorized mount able to point a direction in the sky using two axes. | |
18 | + * **DetectorFocuser**: It is a mechanical motorized motor placed close the detector to able changing the focus. | |
19 | + | |
20 | +The communication chanel is the same for all the components. | |
21 | +The commands are contents to pilot the components. There are two types of commands: | |
22 | + | |
23 | + * **uniform**: From the side of a Guitastro user, the commands are the same for a given component category. For example to slew a mount the command is always "DO RADEC_GOTO". | |
24 | + * **native**: From the side of device controler, the commands are translated by guitastro into the device manufactured grammar. | |
25 | + | |
26 | +A uniform command is constituted by an **action**, an **operation** and optional parameters: | |
27 | + | |
28 | + * **action**: Main actions are GET, SET, DO. GET and SET manage variables and DO manage messages towards the device. | |
29 | + * **operation**: It is a word that depends on the action. | |
30 | + | |
31 | +2. Commands of a component | |
32 | +************************** | |
33 | + | |
34 | +A command can be generalized as component/action/operation/parameters | |
35 | + | |
36 | +When the component/action = MountPointing/SET, the operation is a variable symbol and parameters is the variable value: | |
37 | + | |
38 | +.. code-block:: python | |
39 | + | |
40 | + import guitastro | |
41 | + comp = ComponentMountPointing() | |
42 | + comp.command("SET", "target", "RADEC 12h56m -08d45m") | |
43 | + | |
44 | +When the component/action = MountPointing/GET, the operation is a variable symbol. The result is the variable value: | |
45 | + | |
46 | +.. code-block:: python | |
47 | + | |
48 | + import guitastro | |
49 | + comp = ComponentMountPointing() | |
50 | + comp.command("GET", "target") | |
51 | + | |
52 | +When the component/action/operation = MountPointing/DO/RADEC_GOTO, the operation start a slewing to the target defined in the target variable previously defined: | |
53 | + | |
54 | +.. code-block:: python | |
55 | + | |
56 | + import guitastro | |
57 | + comp = ComponentMountPointing() | |
58 | + comp.command("SET", "target", "RADEC 12h56m -08d45m") | |
59 | + comp.command("DO", "RADEC_GOTO") | |
60 | + | |
61 | + | |
62 | +A given category of component have a list of operations. For example for a MountPointing: | |
63 | + | |
64 | + * **RADEC_COORD**: Returns the current coordinates in the RA,Dec system | |
65 | + * **RADEC_GOTO**: Slew the mount until a target previously defined by a command | |
66 | + | |
67 | +3. Integration of components into devices for Guitastro | |
68 | +******************************************************* | |
69 | + | |
70 | +The device codes a written outside the guitastro module. | |
71 | +For a given device the code is a module. For example, guitastro_device_meade_mounts. | |
72 | +The device modules are downloaded in the site of Guitastro. | |
73 | +To drive a device it is necessary to install guitastro the device module. | |
74 | + | |
75 | +4. Simulation and real devices | |
76 | +****************************** | |
77 | + | |
78 | +Blabla. | ... | ... |
docs/source/index.rst
... | ... | @@ -153,7 +153,15 @@ Describe how to instanciate guitastro classes to use them in a personal Python c |
153 | 153 | |
154 | 154 | using_guitastro_classes |
155 | 155 | |
156 | -9. Class and method documentation | |
156 | +9. Driving devices with guitastro | |
157 | +********************************* | |
158 | + | |
159 | +.. toctree:: | |
160 | + :maxdepth: 3 | |
161 | + | |
162 | + device_definition | |
163 | + | |
164 | +10. Class and method documentation | |
157 | 165 | *********************************** |
158 | 166 | |
159 | 167 | For developers of Python code of GuitAstro: | ... | ... |
docs/source/using_guitastro_classes.rst
... | ... | @@ -62,10 +62,6 @@ We want to compute sum of the angles 2h3m27s and -0d28m12.4s. Display the result |
62 | 62 | 4. Using the Python module guitastro for dates |
63 | 63 | ********************************************** |
64 | 64 | |
65 | -Celme module is located in the folder pyros/src/utils | |
66 | -Examples: pyros/src/utils/report/dates.py | |
67 | -help(Date) | |
68 | - | |
69 | 65 | 4.1. Simple example to convert dates |
70 | 66 | ==================================== |
71 | 67 | |
... | ... | @@ -97,13 +93,6 @@ Date ISO = 2018-10-13T23:13:44.958 |
97 | 93 | 5. Using the Python module guitastro for ephemeris |
98 | 94 | ************************************************** |
99 | 95 | |
100 | -| Celme module is located in the folder pyros/src/utils | |
101 | -| Examples: pyros/src/utils/report/targets.py | |
102 | - | |
103 | -.. code-block:: python | |
104 | - | |
105 | - help(Target) | |
106 | - | |
107 | 96 | 5.1. Simple example to compute Sun coordinates |
108 | 97 | ============================================== |
109 | 98 | ... | ... |
src/guitastro/__init__.py
... | ... | @@ -120,6 +120,26 @@ guitastro.mount |
120 | 120 | .. automodule:: guitastro.mount |
121 | 121 | :members: |
122 | 122 | |
123 | +guitastro.communications | |
124 | +------------------------ | |
125 | +.. automodule:: guitastro.communications | |
126 | + :members: | |
127 | + | |
128 | +guitastro.component | |
129 | +------------------- | |
130 | +.. automodule:: guitastro.component | |
131 | + :members: | |
132 | + | |
133 | +guitastro.component_detector_focuser | |
134 | +------------------------------------ | |
135 | +.. automodule:: guitastro.component_detector_focuser | |
136 | + :members: | |
137 | + | |
138 | +guitastro.component_mount_pointing | |
139 | +------------------------------------ | |
140 | +.. automodule:: guitastro.component_mount_pointing | |
141 | + :members: | |
142 | + | |
123 | 143 | """ |
124 | 144 | from __future__ import (absolute_import, division, print_function, |
125 | 145 | unicode_literals) |
... | ... | @@ -146,6 +166,10 @@ from .filenames import FileNames |
146 | 166 | from .astrotables import AstroTable |
147 | 167 | from .splinefit import Splinefit |
148 | 168 | |
169 | +from .component import Component | |
170 | +from .component_mount_pointing import ComponentMountPointing | |
171 | +from .component_detector_focuser import ComponentDetectorFocuser | |
172 | + | |
149 | 173 | from .camera import Camera |
150 | 174 | from .mount import Mount |
151 | 175 | ... | ... |
src/guitastro/communications.py
... | ... | @@ -48,7 +48,65 @@ class CommunicationException(GuitastroException): |
48 | 48 | class Communication(): |
49 | 49 | """Communication channel manager |
50 | 50 | |
51 | - Usage : Communication("SERIAL", port="//./COM1") | |
51 | + This class allows to open and close TCP or SERIAL communications. | |
52 | + There are methods to put and read messages. | |
53 | + | |
54 | + :Example: | |
55 | + | |
56 | + To get only informations on communication channels SERIAL and TCP: | |
57 | + :: | |
58 | + | |
59 | + >>> chan = Communication("INFOS") | |
60 | + >>> available_serial_ports = chan.get_available_serial_ports() | |
61 | + >>> my_ip_address = chan.get_ip() | |
62 | + | |
63 | + To open a SERIAL port: | |
64 | + :: | |
65 | + | |
66 | + >>> chan = Communication("SERIAL", port="//./COM1", baud_rate=9600) | |
67 | + | |
68 | + To open a TCP port: | |
69 | + :: | |
70 | + | |
71 | + >>> chan = Communication("TCP", hostname="192.168.0.4", port="8080") | |
72 | + | |
73 | + To close an opened port: | |
74 | + :: | |
75 | + | |
76 | + >>> chan.close() | |
77 | + | |
78 | + To send a message through an opened port: | |
79 | + :: | |
80 | + | |
81 | + >>> chan.put("Hello") | |
82 | + | |
83 | + To read a message through an opened port: | |
84 | + :: | |
85 | + | |
86 | + >>> result = chan.read() | |
87 | + | |
88 | + Very often protocols need to add suffix characters to the messages. | |
89 | + The following optional keys can be added to Communication: | |
90 | + | |
91 | + * END_OF_COMMAND_TO_SEND (str): Suffix systematically added at the end of messages sent. | |
92 | + * END_OF_COMMAND_TO_RECEIVE (str): Suffix systematically suppressed at the end of messages received. | |
93 | + * DELAY_INIT_CHAN (float): Delay (seconds) executed just after the communication opening because some devices cannot be joined before. | |
94 | + * DELAY_PUT_READ (float): Delay (seconds) between put and read commands. | |
95 | + | |
96 | + :Example: | |
97 | + | |
98 | + Open a serial port with many parameters, send a message a wait for a response: | |
99 | + | |
100 | + :: | |
101 | + | |
102 | + chan = Communication("SERIAL", port="//./COM1", baud_rate=9600, end_of_command_to_send="\\n", delay_ini_chan=1.8, delay_put_read=0.2) | |
103 | + result = chan.putread("Hello") | |
104 | + | |
105 | + It is possible to open a simulation of the communication channel . | |
106 | + To do that the following optional keys can be added to Communication: | |
107 | + | |
108 | + * REAL (bool): Default value is True | |
109 | + | |
52 | 110 | """ |
53 | 111 | |
54 | 112 | # === Constant for error codes |
... | ... | @@ -178,9 +236,22 @@ class Communication(): |
178 | 236 | self._hostname_chan = self._channel_params["HOSTNAME"] |
179 | 237 | self._real = self._channel_params["REAL"] |
180 | 238 | |
181 | - def get_ip(self): | |
182 | - """ | |
183 | - Tool to return the IP of this computer | |
239 | + def get_ip(self)->str: | |
240 | + """Tool to return the IP of this computer | |
241 | + | |
242 | + Returns: | |
243 | + | |
244 | + The IP addresse of the local machine. | |
245 | + | |
246 | + :Example: | |
247 | + | |
248 | + To get the IP of the computer: | |
249 | + | |
250 | + :: | |
251 | + | |
252 | + >>> chan = Communication("INFOS") | |
253 | + >>> my_ip_address = chan.get_ip() | |
254 | + | |
184 | 255 | """ |
185 | 256 | ip = '127.0.0.1' |
186 | 257 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
... | ... | @@ -207,6 +278,20 @@ class Communication(): |
207 | 278 | |
208 | 279 | def get_available_serial_ports(self): |
209 | 280 | """Return a list of available serial ports of the local computer |
281 | + | |
282 | + Returns: | |
283 | + | |
284 | + The list of serial port names of the local machine | |
285 | + | |
286 | + :Example: | |
287 | + | |
288 | + To get the IP of the computer: | |
289 | + | |
290 | + :: | |
291 | + | |
292 | + >>> chan = Communication("INFOS") | |
293 | + >>> available_serial_ports = chan.get_available_serial_ports() | |
294 | + | |
210 | 295 | """ |
211 | 296 | # -- |
212 | 297 | prefix_myserial_ports = "" |
... | ... | @@ -245,6 +330,8 @@ class Communication(): |
245 | 330 | return self.NO_ERROR |
246 | 331 | |
247 | 332 | def open_chan(self): |
333 | + """Open the channel | |
334 | + """ | |
248 | 335 | err = self.NO_ERROR |
249 | 336 | if self._real == False: |
250 | 337 | fid = "simulation" |
... | ... | @@ -315,6 +402,8 @@ class Communication(): |
315 | 402 | return self.NO_ERROR |
316 | 403 | |
317 | 404 | def close_chan(self): |
405 | + """Close the channel | |
406 | + """ | |
318 | 407 | err = self.NO_ERROR |
319 | 408 | fid = self._fid_chan |
320 | 409 | if self._fid_chan == None: |
... | ... | @@ -343,6 +432,11 @@ class Communication(): |
343 | 432 | |
344 | 433 | def put_chan(self, cmd: str): |
345 | 434 | """ Send a message into the opened channel |
435 | + | |
436 | + Args: | |
437 | + | |
438 | + cmd: Message to send. Suffix defined by the option key END_OF_COMMAND_TO_SEND is added inside the method. | |
439 | + | |
346 | 440 | """ |
347 | 441 | # --- check the opening of the serial port |
348 | 442 | err, fid = self.check_chan() |
... | ... | @@ -361,6 +455,13 @@ class Communication(): |
361 | 455 | return (err, cmd) |
362 | 456 | |
363 | 457 | def read_chan(self): |
458 | + """ Receive a message from the opened channel | |
459 | + | |
460 | + Returns: | |
461 | + | |
462 | + The string of the message read. | |
463 | + | |
464 | + """ | |
364 | 465 | # --- check the opening of the serial port |
365 | 466 | err, fid = self.check_chan() |
366 | 467 | lignes = "" ; # fid.readlines() |
... | ... | @@ -446,16 +547,37 @@ class Communication(): |
446 | 547 | delay_init_chan = property(_get_delay_init_chan, _set_delay_init_chan) |
447 | 548 | verbose_chan = property(_get_verbose_chan, _set_verbose_chan) |
448 | 549 | |
449 | - def putread_chan(self, cmd, index=-1): | |
450 | - return self.putread_chan_check(cmd, index) | |
550 | + def putread_chan(self, cmd:str, index:int=-1): | |
551 | + """ Send and Receive a message from the opened channel | |
552 | + | |
553 | + Args: | |
451 | 554 | |
452 | - def putread_chan_nocheck(self, cmd, index=-1): | |
555 | + cmd: The message to send | |
556 | + index: >0 to extract only the word returned at the index position | |
557 | + | |
558 | + Returns: | |
559 | + | |
560 | + A string containing the response received. | |
453 | 561 | """ |
454 | - cmd is the message to send | |
455 | - index >0 to extract only the word returned at the index position | |
562 | + return self.putread_chan_check(cmd, index) | |
563 | + | |
564 | + def putread_chan_nocheck(self, cmd:str, index:int=-1): | |
565 | + """ Send and Receive a message from the opened channel | |
566 | + | |
567 | + No lock version. See comments of the putread_chan_check | |
568 | + | |
569 | + Args: | |
570 | + | |
571 | + cmd: The message to send | |
572 | + index: >0 to extract only the word returned at the index position | |
573 | + | |
574 | + Returns: | |
575 | + | |
576 | + A string containing the response received. | |
456 | 577 | """ |
457 | 578 | if self.verbose_chan==True: |
458 | - self.log.self.log.print("Putread: "+cmd) | |
579 | + #self.log.self.log.print("Putread: "+cmd) | |
580 | + pass | |
459 | 581 | self._lock_putread = True |
460 | 582 | self.put_chan(cmd) |
461 | 583 | time.sleep(self._delay_put_read) |
... | ... | @@ -474,11 +596,18 @@ class Communication(): |
474 | 596 | |
475 | 597 | #def putread_chan_lock_verify(self, cmd, index=-1): |
476 | 598 | def putread_chan_check(self, cmd, index=-1): |
477 | - """ | |
478 | - Version with a lock to avoid multiple calls to the controller. | |
599 | + """ Send and Receive a message from the opened channel | |
600 | + | |
601 | + Same as putread_chan but a lock is added to avoid multiple calls to the controller. | |
602 | + | |
603 | + Args: | |
604 | + | |
605 | + cmd: The message to send | |
606 | + index: >0 to extract only the word returned at the index position | |
607 | + | |
608 | + Returns: | |
479 | 609 | |
480 | - cmd is the message to send | |
481 | - index >0 to extract only the word returned at the index position | |
610 | + A string containing the response received. | |
482 | 611 | """ |
483 | 612 | if self.verbose_chan==True: |
484 | 613 | # self.log.self.log.print("Putread: "+cmd) |
... | ... | @@ -494,7 +623,7 @@ class Communication(): |
494 | 623 | time.sleep(0.05) |
495 | 624 | dt = time.time() - t0 |
496 | 625 | if dt>2.0: |
497 | - self.log.print("Serial port locked timeout for {}".format(cmd)) | |
626 | + #self.log.print("Serial port locked timeout for {}".format(cmd)) | |
498 | 627 | break |
499 | 628 | # --- verify the lock state |
500 | 629 | if self._lock_putread == False: | ... | ... |
src/guitastro/component.py
... | ... | @@ -56,8 +56,9 @@ class Component(ComponentException): |
56 | 56 | |
57 | 57 | A command of a component is constituted by: |
58 | 58 | |
59 | - * Action : A string word as GET, SET, DO | |
60 | - * Parameters : List or/and dict depending the action | |
59 | + * Action: A string word as GET, SET, DO | |
60 | + * Operation: A string word that dependis on the action | |
61 | + * Parameters: List or/and dict depending the action/operation | |
61 | 62 | |
62 | 63 | A command is sent to the component using the method command: |
63 | 64 | |
... | ... | @@ -99,7 +100,7 @@ class Component(ComponentException): |
99 | 100 | Note that the parameters of an operation should be assigned by the Action SET |
100 | 101 | before calling DO. |
101 | 102 | |
102 | - :Example: | |
103 | + :Example: | |
103 | 104 | |
104 | 105 | :: |
105 | 106 | |
... | ... | @@ -222,8 +223,11 @@ class Component(ComponentException): |
222 | 223 | return res |
223 | 224 | |
224 | 225 | def parameters(self, action="", operation=""): |
225 | - """Get the list of operations for a given action | |
226 | - or get the description of a given operation. | |
226 | + """Get the list of operations for a given action or get the description of a given operation. | |
227 | + | |
228 | + Args: | |
229 | + | |
230 | + action: If not given, returns the list of Actions | |
227 | 231 | """ |
228 | 232 | actions = self.actions |
229 | 233 | if action=="": |
... | ... | @@ -244,7 +248,19 @@ class Component(ComponentException): |
244 | 248 | def command(self, action:str, *args, **kwargs): |
245 | 249 | """General method to send command. |
246 | 250 | |
247 | - Action must be a str amongst elements of the list returned by the method prop | |
251 | + Args: | |
252 | + | |
253 | + action: A str amongst elements of the list returned by the method prop | |
254 | + *args: args[0] should be an operation | |
255 | + **kwargs: Dictionary of options associated to the operation | |
256 | + | |
257 | + Returns: | |
258 | + | |
259 | + The result of the command. | |
260 | + | |
261 | + Use the method parameters(action) to get the available operations for a given action. | |
262 | + The kwargs are defined for each operation. | |
263 | + | |
248 | 264 | """ |
249 | 265 | result = None |
250 | 266 | action = action.upper() | ... | ... |
src/guitastro/component_mount_pointing.py
... | ... | @@ -83,7 +83,7 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
83 | 83 | # --- call the real |
84 | 84 | kwargs = {} |
85 | 85 | kwargs['computed'] = (ra, dec, equinox, epoch, dra, ddec) |
86 | - self._my_do(*args, **kwargs) | |
86 | + result = self._my_do(*args, **kwargs) | |
87 | 87 | # --- update the motion |
88 | 88 | self._set("motion", "slewing") |
89 | 89 | else: |
... | ... | @@ -96,10 +96,12 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
96 | 96 | # TODO |
97 | 97 | # --- update the motion |
98 | 98 | self._set("motion", "stoped") |
99 | + result = self._my_do(*args, **kwargs) | |
99 | 100 | elif operation == "RADEC_COORD": |
100 | 101 | # --- make the simulation |
101 | 102 | # TODO |
102 | 103 | self.log = f"start simulation of {operation}" |
104 | + result = self._my_do(*args, **kwargs) | |
103 | 105 | result = "12h00m45.78s -06d55m23.2s" |
104 | 106 | return self._fresult(result) |
105 | 107 | ... | ... |
src/guitastro/ephemeris.py
... | ... | @@ -443,21 +443,33 @@ class Ephemeris(EphemerisException, GuitastroTools): |
443 | 443 | return sra, sdec, equinox, t0 |
444 | 444 | |
445 | 445 | def radec(self, target: str, **kwargs)-> tuple: |
446 | - """Return ra, dec, euqinox, epoch of a target. | |
446 | + """Return ra, dec, equinox, epoch of a target. | |
447 | 447 | |
448 | - target is a string that starts with a keyword: | |
448 | + Args: | |
449 | 449 | |
450 | - * RADEC: to indicate an equatorial Right Ascension, Declination position. | |
451 | - * CELESTRACK: to indicate a satellite name in the Celestrack TLE files. | |
452 | - * DATERADECS: to indicate a list of equatorial Right Ascension, Declination positions. | |
453 | - * TLE: to indicate a satellite defined by its TLE (Two Line Elements). | |
450 | + target: A string that starts with a keyword: | |
454 | 451 | |
455 | - kwargs keys can be: | |
452 | + * 'RADEC': Followed by an equatorial Right Ascension, Declination position. | |
453 | + * 'RADECDRIFT': Followed by an equatorial Right Ascension, Declination position and the drift. | |
454 | + * 'CELESTRACK' or 'TLEFILES': Followed by a satellite name in the Celestrack TLE files. | |
455 | + * 'DATERADECS': Followed by a list of equatorial Right Ascension, Declination positions. | |
456 | + * 'TLE': Followed by a satellite defined by its TLE (Two Line Elements). | |
457 | + * 'GCNC': Followed by a number which is a GRB name (e.g. GCNC 990123) to search information in GCN circulars. | |
458 | + * 'MPC': Followed by a name which is a Solar System Body. | |
456 | 459 | |
457 | - * date: To compute ephemeris for a given date ("now" for now). | |
458 | - * unit_ra: To indicate the output format (see Angle) | |
459 | - * unit_dec: To indicate the output format (see Angle) | |
460 | - * target_type: To indicate the input format if the keyword is not indicated in the target string. | |
460 | + **kwargs: A dictionary of options, keys can be: | |
461 | + | |
462 | + * 'date': To compute ephemeris for a given date ("now" for now). | |
463 | + * 'unit_ra': To indicate the output format (see Angle) | |
464 | + * 'unit_dec': To indicate the output format (see Angle) | |
465 | + * 'target_type': To indicate the input format if the keyword is not indicated in the target string. | |
466 | + | |
467 | + Returns: | |
468 | + | |
469 | + ra: Target Right Ascention position at the given date for a given equinox | |
470 | + dec: Target Declination at the given date for a given equinox | |
471 | + equinox: Equinox of the position | |
472 | + epoch: Date of the position when the object is moving | |
461 | 473 | |
462 | 474 | """ |
463 | 475 | equinox = "J2000" |
... | ... | @@ -724,6 +736,21 @@ class Ephemeris(EphemerisException, GuitastroTools): |
724 | 736 | |
725 | 737 | def radec_speed(self, target, **kwargs): |
726 | 738 | """Return ra, dec J2000 of a target as method radec and add the speed (deg/s) |
739 | + | |
740 | + Args: | |
741 | + | |
742 | + target: See radec for explanations | |
743 | + **kwargs: See radec for explanations | |
744 | + | |
745 | + Returns: | |
746 | + | |
747 | + ra: Target Right Ascention position at the given date for a given equinox | |
748 | + dec: Target Declination at the given date for a given equinox | |
749 | + equinox: Equinox of the position | |
750 | + epoch: Date of the position when the object is moving | |
751 | + dra: Velocity on Right Ascension axis (deg/s) | |
752 | + ddec: Velocity on Declination axis (deg/s) | |
753 | + | |
727 | 754 | """ |
728 | 755 | ra, dec, equinox, epoch = self.radec(target, **kwargs) |
729 | 756 | target = str(target) |
... | ... | @@ -762,6 +789,16 @@ class Ephemeris(EphemerisException, GuitastroTools): |
762 | 789 | |
763 | 790 | def altitude2tp(self, alti:float, p0m:float=101325): |
764 | 791 | """Compute the theoretical pressure and temperature in the Earth atmosphere given an altitude |
792 | + | |
793 | + Args: | |
794 | + | |
795 | + alti: The altitude of the observation site in meters | |
796 | + p0m: The pressure at the sea level. Default is 101325 Pascal. | |
797 | + | |
798 | + Returns: | |
799 | + | |
800 | + pressure: The pressure in Pascal | |
801 | + temperature: The temperature in Kelvin | |
765 | 802 | """ |
766 | 803 | tk0m=273.15+15 |
767 | 804 | if alti<11000: |
... | ... | @@ -832,6 +869,52 @@ class Ephemeris(EphemerisException, GuitastroTools): |
832 | 869 | |
833 | 870 | def target2night(self, target, night:str, ephem_sun:dict=None, ephem_moon:dict=None, **kwargs)->dict: |
834 | 871 | """Compute the ephemeris at every second of local coodinates for a night |
872 | + | |
873 | + Two computations are importants: | |
874 | + | |
875 | + * 'observability': An integer defining why the target is visible or not. | |
876 | + * 'observability': A float value from 0 (not observable) to 100 (best conditions to observe) | |
877 | + | |
878 | + Args: | |
879 | + | |
880 | + target: A target in the formalism of the method radec | |
881 | + night: A night symbol (e.g. 20230330) | |
882 | + ephem_sun: A dictionary result of the method called target2night("sun", night) | |
883 | + ephem_moon: A dictionary result of the method called target2night("moon", night) | |
884 | + kwargs: A dictionary of options: | |
885 | + | |
886 | + * horizon: An object of the class Horizon of Guitastro that defines the horizon line. | |
887 | + * preference: A string "bestelev" or "immediate" to compute the observability | |
888 | + * duskelev: A float, the elevation of the Sun defining the start and end of the night. | |
889 | + | |
890 | + Returns: | |
891 | + | |
892 | + A dictionary: | |
893 | + | |
894 | + * 'night': String of the night | |
895 | + * 'home': String of the GPS position | |
896 | + * 'target': String of the input target | |
897 | + * 'ndate': Number of dates used to compute amers before interpolations | |
898 | + * 'jd': Numpy array of Julian days | |
899 | + * 'alt': Numpy array of the elevation (deg) | |
900 | + * 'az': Numpy array of the azimut (deg) | |
901 | + * 'ha': Numpy array of the apparent hour angle (deg) | |
902 | + * 'dec': Numpy array of the apparent declination (deg) | |
903 | + * 'ra_equinox': Numpy array of the Right Ascension at the input equinox (deg) | |
904 | + * 'dec_equinox': Numpy array of the Declination at the input equinox (deg) | |
905 | + * 'cosphi': Numpy array of cos(ra_equinox) | |
906 | + * 'sinphi': Numpy array of sin(ra_equinox) | |
907 | + * 'costheta': Numpy array of cos(dec_equinox) | |
908 | + * 'sintheta': Numpy array of sin(dec_equinox) | |
909 | + * 'distsun': Numpy array of the distance from the Sun (deg) | |
910 | + * 'distmoon': Numpy array of the distance from the Moon (deg) | |
911 | + * 'visibility': Numpy array of the visibility | |
912 | + * 'observability': Numpy array of the observability (>0 means observable) | |
913 | + * 'horizon': Numpy array of horizon elevations (deg) | |
914 | + | |
915 | + The Numpy arrays are 1D of 86400 elements. | |
916 | + if ephem_sun==None or ephem_moon==None, the distances between the target and the Sun and Moon will not be calculated. | |
917 | + | |
835 | 918 | """ |
836 | 919 | nsec = 86400 |
837 | 920 | if ephem_sun==None or ephem_moon==None: |
... | ... | @@ -845,7 +928,7 @@ class Ephemeris(EphemerisException, GuitastroTools): |
845 | 928 | hor_az = np.arange(0, 361) |
846 | 929 | hor_elev = np.zeros(len(hor_az)) |
847 | 930 | if 'preference' in kwargs.keys(): |
848 | - preference = kwargs['preference'] | |
931 | + preference = kwargs['preference'].lower() | |
849 | 932 | else: |
850 | 933 | preference="bestelev" |
851 | 934 | if 'duskelev' in kwargs.keys(): |
... | ... | @@ -961,41 +1044,42 @@ class Ephemeris(EphemerisException, GuitastroTools): |
961 | 1044 | sinthetas = aangles[9] |
962 | 1045 | distsuns = aangles[10] |
963 | 1046 | distmoons = aangles[11] |
964 | - # --- visibility | |
965 | - disponibilitys = np.zeros(nsec) | |
1047 | + # --- observability and visibility | |
966 | 1048 | visibilitys = np.zeros(nsec) |
1049 | + observabilitys = np.zeros(nsec) | |
967 | 1050 | horizons = np.zeros(nsec) |
968 | 1051 | if sunmoon==True: |
1052 | + # --- visibility | |
969 | 1053 | kk = 0 |
970 | 1054 | for alt, az in zip(alts, azs): |
971 | 1055 | k = int(np.floor(az))%360 |
972 | 1056 | altmini = hor_elev[k] |
973 | 1057 | horizons[kk] = altmini |
974 | 1058 | if alt < altmini: |
975 | - disponibilitys[kk] += 1 | |
1059 | + visibilitys[kk] += 1 | |
976 | 1060 | if ephem_sun['alt'][kk] > duskelev: |
977 | - disponibilitys[kk] += 2 | |
1061 | + visibilitys[kk] += 2 | |
978 | 1062 | kk += 1 |
979 | - disponibilitys = np.where(distsuns > 30, disponibilitys, disponibilitys+4) | |
980 | - disponibilitys = np.where(distmoons > 30, disponibilitys, disponibilitys+8) | |
981 | - # --- visibility | |
1063 | + visibilitys = np.where(distsuns > 30, visibilitys, visibilitys+4) | |
1064 | + visibilitys = np.where(distmoons > 30, visibilitys, visibilitys+8) | |
1065 | + # --- observability | |
982 | 1066 | nvis = 0 |
983 | 1067 | altmaxi = 0 |
984 | 1068 | for kk in range(nsec): |
985 | - if disponibilitys[kk] == 0: | |
1069 | + if visibilitys[kk] == 0: | |
986 | 1070 | nvis += 1 |
987 | 1071 | if alts[kk] > altmaxi: |
988 | 1072 | altmaxi = alts[kk] |
989 | 1073 | if preference=="immediate": |
990 | 1074 | kvis = nvis |
991 | 1075 | for kk in range(nsec): |
992 | - if disponibilitys[kk] == 0: | |
993 | - visibilitys[kk] = kvis/nvis*100 | |
1076 | + if visibilitys[kk] == 0: | |
1077 | + observabilitys[kk] = kvis/nvis*100 | |
994 | 1078 | kvis -= 1 |
995 | 1079 | if preference=="bestelev": |
996 | 1080 | for kk in range(nsec): |
997 | - if disponibilitys[kk] == 0: | |
998 | - visibilitys[kk] = alts[kk]/altmaxi*100 | |
1081 | + if visibilitys[kk] == 0: | |
1082 | + observabilitys[kk] = alts[kk]/altmaxi*100 | |
999 | 1083 | # --- dictionary |
1000 | 1084 | eph = {} |
1001 | 1085 | eph['night'] = night |
... | ... | @@ -1015,8 +1099,8 @@ class Ephemeris(EphemerisException, GuitastroTools): |
1015 | 1099 | eph['sintheta'] = sinthetas |
1016 | 1100 | eph['distsun'] = distsuns |
1017 | 1101 | eph['distmoon'] = distmoons |
1018 | - eph['disponibility'] = disponibilitys | |
1019 | 1102 | eph['visibility'] = visibilitys |
1103 | + eph['observability'] = observabilitys | |
1020 | 1104 | eph['horizon'] = horizons |
1021 | 1105 | return eph |
1022 | 1106 | |
... | ... | @@ -1156,6 +1240,14 @@ if __name__ == "__main__": |
1156 | 1240 | Compute the ephemeris of a target along all a night |
1157 | 1241 | """ |
1158 | 1242 | import matplotlib.pyplot as plt |
1243 | + def compute_hours(ephem): | |
1244 | + jd0 = ephem['jd'][0] | |
1245 | + frac = jd0 - np.floor(jd0) | |
1246 | + offset = frac - 0.5 | |
1247 | + hours = (ephem['jd'] - jd0 + offset)*24 | |
1248 | + date = Date(jd0).iso(0) | |
1249 | + return hours, date | |
1250 | + # --- | |
1159 | 1251 | eph = Ephemeris() |
1160 | 1252 | eph.set_home("guitalens") |
1161 | 1253 | night = "20230320" |
... | ... | @@ -1173,7 +1265,6 @@ if __name__ == "__main__": |
1173 | 1265 | ephem_sun = eph.target2night(target, night) |
1174 | 1266 | dt = time.time()-t0 |
1175 | 1267 | print(f"SUN dt={dt}") |
1176 | - plt.plot(ephem_sun['jd'], ephem_sun['alt'], "y-") | |
1177 | 1268 | # --- moon |
1178 | 1269 | target = "moon" |
1179 | 1270 | t0 = time.time() |
... | ... | @@ -1186,12 +1277,12 @@ if __name__ == "__main__": |
1186 | 1277 | ephem = eph.target2night(target, night, ephem_sun, ephem_moon, horizon=hor, preference=preference, duskelev=duskelev) |
1187 | 1278 | dt = time.time()-t0 |
1188 | 1279 | print(f"TARGET dt={dt}") |
1189 | - #plt.plot(ephem['jd'], ephem['ha'], "r:") | |
1190 | - #plt.plot(ephem['jd'], ephem['dec'], "r-") | |
1191 | - plt.plot(ephem['jd'], ephem['alt'], "b-") | |
1192 | - #plt.plot(ephem['jd'], ephem['az'], ":-") | |
1193 | - plt.plot(ephem['jd'], ephem['disponibility'], "k:") | |
1194 | - plt.plot(ephem['jd'], ephem['visibility'], "k-") | |
1195 | - plt.plot(ephem['jd'], ephem['horizon'], "g-") | |
1280 | + hours, date = compute_hours(ephem) | |
1281 | + plt.plot(hours, ephem_sun['alt'], "y-") | |
1282 | + plt.plot(hours, ephem['alt'], "b-") | |
1283 | + plt.plot(hours, ephem['horizon'], "g-") | |
1284 | + plt.plot(hours, ephem['visibility'], "k:") | |
1285 | + plt.plot(hours, ephem['observability'], "k-") | |
1196 | 1286 | plt.grid() |
1197 | - | |
1287 | + plt.ylabel('Degrees') | |
1288 | + plt.xlabel(f'Hours since {date}') | ... | ... |
src/guitastro/filenames.py
... | ... | @@ -1128,7 +1128,7 @@ class FileNames(FileNamesException, GuitastroTools): |
1128 | 1128 | fn = FileNames() |
1129 | 1129 | fn.naming_date("2023-01-03T12:43:27.876983") |
1130 | 1130 | |
1131 | - The result should be {'iso': '2023-01-03T12:43:27.876983', 'iso_': '2023_01_03_12_43_27_876983', 'yyyy': '2023', 'mm': '43', 'dd': '03', 'hh': '12', 'ss': '27', 'yyyymmdd': '20230103', 'hhmmss': '124327', 'hhmmssssssss': '124327876983'} | |
1131 | + The result should be {'iso': '2023-01-03T12:43:27.876983', 'iso\_': '2023_01_03_12_43_27_876983', 'yyyy': '2023', 'mm': '43', 'dd': '03', 'hh': '12', 'ss': '27', 'yyyymmdd': '20230103', 'hhmmss': '124327', 'hhmmssssssss': '124327876983'} | |
1132 | 1132 | """ |
1133 | 1133 | date = Date(date); |
1134 | 1134 | digits = date.digits(6) | ... | ... |