Commit ec442f7e4e3211cc018769e7d3f1ba62da88de5f
1 parent
120d4bfd
Exists in
master
Debug components Focuser and MountPointing.
Showing
6 changed files
with
554 additions
and
369 deletions
Show diff stats
src/guitastro/component.py
... | ... | @@ -319,7 +319,39 @@ class Component(ComponentException): |
319 | 319 | categories = ['MountPointing', 'DetectorFocuser'] |
320 | 320 | |
321 | 321 | # ===================================================================== |
322 | - # properties | |
322 | + # init | |
323 | + # ===================================================================== | |
324 | + | |
325 | + def __init__(self, *args, **kwargs): | |
326 | + # --- | |
327 | + self._queue = Queue() | |
328 | + self.database = ComponentDataBase(self) | |
329 | + self.database.start() | |
330 | + time.sleep(0.2) | |
331 | + # --- | |
332 | + self.init(*args, **kwargs) | |
333 | + | |
334 | + def init(self, *args, **kwargs): | |
335 | + self._my_init1(*args, **kwargs) | |
336 | + self._my_init2(*args, **kwargs) | |
337 | + | |
338 | + def _my_init1(self, *args, **kwargs): | |
339 | + # before instanciation of the simulator | |
340 | + pass | |
341 | + | |
342 | + def _my_init2(self, *args, **kwargs): | |
343 | + # after instanciation of the simulator to configure it. | |
344 | + pass | |
345 | + | |
346 | + # ===================================================================== | |
347 | + # del | |
348 | + # ===================================================================== | |
349 | + | |
350 | + def __del__(self): | |
351 | + self.database.stop() | |
352 | + | |
353 | + # ===================================================================== | |
354 | + # properties: real, channel, category, action, verbose, log | |
323 | 355 | # ===================================================================== |
324 | 356 | |
325 | 357 | def _get_real(self)->bool: |
... | ... | @@ -415,7 +447,7 @@ class Component(ComponentException): |
415 | 447 | log = property(_get_log, _set_log, _del_log) |
416 | 448 | |
417 | 449 | # ===================================================================== |
418 | - # public methods | |
450 | + # prop | |
419 | 451 | # ===================================================================== |
420 | 452 | |
421 | 453 | def prop(self): |
... | ... | @@ -432,6 +464,10 @@ class Component(ComponentException): |
432 | 464 | res['DO']['NOTHING'] = "Nothing to do" |
433 | 465 | return res |
434 | 466 | |
467 | + # ===================================================================== | |
468 | + # parameters (get/set param) | |
469 | + # ===================================================================== | |
470 | + | |
435 | 471 | def parameters(self, action="", operation=""): |
436 | 472 | """Get the list of operations for a given action or get the description of a given operation. |
437 | 473 | |
... | ... | @@ -455,13 +491,17 @@ class Component(ComponentException): |
455 | 491 | value = self.prop()[action][operation] |
456 | 492 | return value |
457 | 493 | |
494 | + # ===================================================================== | |
495 | + # command (execute an action) | |
496 | + # ===================================================================== | |
497 | + | |
458 | 498 | def command(self, action:str, *args, **kwargs): |
459 | 499 | """General method to send command. |
460 | 500 | |
461 | 501 | Args: |
462 | 502 | |
463 | 503 | action: A str amongst elements of the list returned by the method prop |
464 | - *args: args[0] should be an operation | |
504 | + *args: args[0] should be an operation (type str) | |
465 | 505 | **kwargs: Dictionary of options associated to the operation |
466 | 506 | |
467 | 507 | Returns: |
... | ... | @@ -487,9 +527,6 @@ class Component(ComponentException): |
487 | 527 | result = self._set(*args, **kwargs) |
488 | 528 | return result |
489 | 529 | |
490 | - def init(self, *args, **kwargs): | |
491 | - self._my_init1(*args, **kwargs) | |
492 | - self._my_init2(*args, **kwargs) | |
493 | 530 | |
494 | 531 | # ===================================================================== |
495 | 532 | # protected methods |
... | ... | @@ -521,6 +558,10 @@ class Component(ComponentException): |
521 | 558 | val = node_or_string |
522 | 559 | return val |
523 | 560 | |
561 | + # ===================================================================== | |
562 | + # fresult to manage log | |
563 | + # ===================================================================== | |
564 | + | |
524 | 565 | def _fresult(self, value): |
525 | 566 | """Formated result for returns |
526 | 567 | """ |
... | ... | @@ -533,6 +574,10 @@ class Component(ComponentException): |
533 | 574 | del(self.log) |
534 | 575 | return res |
535 | 576 | |
577 | + # ===================================================================== | |
578 | + # Action set | |
579 | + # ===================================================================== | |
580 | + | |
536 | 581 | def _set(self, *args, **kwargs): |
537 | 582 | args, kwargs = self._my_set(*args, **kwargs) |
538 | 583 | self._verify_nargs(2, *args) |
... | ... | @@ -549,6 +594,10 @@ class Component(ComponentException): |
549 | 594 | def _my_set(self, *args, **kwargs): |
550 | 595 | return args, kwargs |
551 | 596 | |
597 | + # ===================================================================== | |
598 | + # Action get | |
599 | + # ===================================================================== | |
600 | + | |
552 | 601 | def _get(self, *args, **kwargs): |
553 | 602 | if len(args)==0: |
554 | 603 | return self.database.query() |
... | ... | @@ -562,6 +611,10 @@ class Component(ComponentException): |
562 | 611 | def _my_get(self, *args, **kwargs): |
563 | 612 | return None |
564 | 613 | |
614 | + # ===================================================================== | |
615 | + # Action do | |
616 | + # ===================================================================== | |
617 | + | |
565 | 618 | def _do(self, *args, **kwargs): |
566 | 619 | result = None |
567 | 620 | self._verify_nargs(1, *args) |
... | ... | @@ -581,31 +634,6 @@ class Component(ComponentException): |
581 | 634 | def _my_do(self, *args, **kwargs): |
582 | 635 | return None |
583 | 636 | |
584 | - def _my_init1(self, *args, **kwargs): | |
585 | - # before instanciation of the simulator | |
586 | - pass | |
587 | - | |
588 | - def _my_init2(self, *args, **kwargs): | |
589 | - # after instanciation of the simulator to configure it. | |
590 | - pass | |
591 | - | |
592 | - # ===================================================================== | |
593 | - # ===================================================================== | |
594 | - # Special methods | |
595 | - # ===================================================================== | |
596 | - # ===================================================================== | |
597 | - | |
598 | - def __init__(self, *args, **kwargs): | |
599 | - # --- | |
600 | - self._queue = Queue() | |
601 | - self.database = ComponentDataBase(self) | |
602 | - self.database.start() | |
603 | - time.sleep(0.2) | |
604 | - # --- | |
605 | - self.init(*args, **kwargs) | |
606 | - | |
607 | - def __del__(self): | |
608 | - self.database.stop() | |
609 | 637 | |
610 | 638 | # ##################################################################### |
611 | 639 | # ##################################################################### | ... | ... |
src/guitastro/component_detector_focuser.py
1 | - | |
1 | +import time | |
2 | 2 | |
3 | 3 | try: |
4 | 4 | from .home import Home |
... | ... | @@ -62,6 +62,70 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
62 | 62 | # methods |
63 | 63 | # ===================================================================== |
64 | 64 | |
65 | + # ------------ init | |
66 | + | |
67 | + def _my_init_params(self, param_optionals: dict, focuser_types: dict): | |
68 | + # --- This method could be overriden | |
69 | + return param_optionals, focuser_types | |
70 | + | |
71 | + def init(self, *args, **kwargs): | |
72 | + # --- Dico of optional parameters for all axis_types | |
73 | + param_optionals = {} | |
74 | + param_optionals["MODEL"] = (str, "") | |
75 | + param_optionals["MANUFACTURER"] = (str, "") | |
76 | + param_optionals["SERIAL_NUMBER"] = (str, "") | |
77 | + param_optionals["REAL"] = (bool, False) | |
78 | + param_optionals["DESCRIPTION"] = (str, "No description.") | |
79 | + param_optionals["SITE"] = (Siteobs,"GPS 0 E 45 100") | |
80 | + param_optionals["LIM_INF"] = (float,0) | |
81 | + param_optionals["LIM_SUP"] = (float,100000) | |
82 | + param_optionals["LOGO_FILE"] = (str,"") | |
83 | + param_optionals["CLIENT_DATA"] = (any,None) | |
84 | + param_optionals["INC_PER_MOTOR_REV"] = (float, 1000.0) | |
85 | + param_optionals["MM_PER_MOTOR_REV"] = (float, 2.0) | |
86 | + # --- Dico of axis_types and their parameters | |
87 | + focuser_types = {} | |
88 | + focuser_types["Z"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Detector focuser"]} } | |
89 | + # --- Add params and types when inherited | |
90 | + param_optionals, focuser_types = self._my_init_params(param_optionals, focuser_types) | |
91 | + # --- Decode args and kwargs parameters | |
92 | + self._focuser_params = self.decode_args_kwargs(0, focuser_types, param_optionals, *args, **kwargs) | |
93 | + # === | |
94 | + self._mount_type = self._focuser_params["SELECTED_ARG"] | |
95 | + self._name = self._focuser_params["NAME"] | |
96 | + self._description = self._focuser_params["DESCRIPTION"] | |
97 | + self._model = self._focuser_params["MODEL"] | |
98 | + self._manufacturer = self._focuser_params["MANUFACTURER"] | |
99 | + self._serial_number= self._focuser_params["SERIAL_NUMBER"] | |
100 | + # === local | |
101 | + if str(type(self._focuser_params["SITE"])) == "<class 'siteobs.Siteobs'>": | |
102 | + self.siteobs = self._focuser_params["SITE"] | |
103 | + else: | |
104 | + self.siteobs = Home(self._focuser_params["SITE"]) | |
105 | + # --- Update the database using the queue | |
106 | + param = {} | |
107 | + param["motion_simu"] = self.MOTION_STATE_UNKNOWN | |
108 | + param["motion_real"] = self.MOTION_STATE_UNKNOWN | |
109 | + param["unit"] = "inc" # inc, rot, lin, phy | |
110 | + param["target"] = 0 # unit | |
111 | + param["speed_slew"] = 100 # unit/s | |
112 | + self._queue.put(param) | |
113 | + time.sleep(0.02) # wait to be sure the thread has record the new value | |
114 | + # --- | |
115 | + self._inc_per_motor_rev = self._focuser_params["INC_PER_MOTOR_REV"] | |
116 | + self._mm_per_motor_rev = self._focuser_params["MM_PER_MOTOR_REV"] | |
117 | + # --- | |
118 | + self._my_init1(*args, **kwargs) | |
119 | + # --- Motor simulator | |
120 | + self._maxis = Motoraxis("LIN", name = "Focuser", inc_per_motor_rev=self._inc_per_motor_rev, inc0=0, senseinc=1, real=False, mm_per_motor_rev=self._mm_per_motor_rev) | |
121 | + self._maxis.define_phy = [self.nat2phy, self.phy2nat] | |
122 | + self._maxis.phy_unit = "inch" | |
123 | + # --- | |
124 | + self._my_init2(*args, **kwargs) | |
125 | + # --- | |
126 | + | |
127 | + # ------------ prop | |
128 | + | |
65 | 129 | def prop(self): |
66 | 130 | """Component property concrete method |
67 | 131 | """ |
... | ... | @@ -74,66 +138,15 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
74 | 138 | res['DO']['STOP'] = "Stop the focus motion" |
75 | 139 | return res |
76 | 140 | |
77 | - def val2inc(self, val:float): | |
78 | - """Conversion coder from unit to increment | |
79 | - | |
80 | - Args: | |
81 | - | |
82 | - val: Value in unit defined by the variable 'unit'. | |
83 | - | |
84 | - Returns: | |
85 | - | |
86 | - Increments. | |
87 | - | |
88 | - """ | |
89 | - unit = self._get("unit") | |
90 | - inc = val | |
91 | - if unit=="phy": | |
92 | - phy = val | |
93 | - xnat2phy, xphy2nat = self._maxis.define_phy | |
94 | - rot, lin = xphy2nat(phy) | |
95 | - elif unit=="lin": | |
96 | - lin = val | |
97 | - rot = self._maxis.lin2rot(lin, self._maxis.SAVE_NONE) | |
98 | - elif unit=="rot": | |
99 | - rot = val | |
100 | - if unit=="rot" or unit=="lin" or unit=="phy": | |
101 | - inc = self._maxis.rot2inc(rot, self._maxis.SAVE_NONE) | |
102 | - return inc | |
103 | - | |
104 | - def inc2val(self, inc:float): | |
105 | - """Conversion coder from increment to unit | |
106 | - | |
107 | - Args: | |
108 | - | |
109 | - inc: Increment. | |
110 | - | |
111 | - Returns: | |
112 | - | |
113 | - Value in unit defined by the variable 'unit'. | |
114 | - | |
115 | - """ | |
116 | - unit = self._get("unit") | |
117 | - val = inc | |
118 | - if unit=="rot" or unit=="lin" or unit=="phy": | |
119 | - rot = self._maxis.inc2rot(inc, self._maxis.SAVE_AS_SIMU) | |
120 | - val = rot | |
121 | - if unit=="lin" or unit=="phy": | |
122 | - lin = self._maxis.rot2lin(rot, self._maxis.SAVE_AS_SIMU) | |
123 | - val = lin | |
124 | - if unit=="phy": | |
125 | - xnat2phy, xphy2nat = self._maxis.define_phy | |
126 | - self._maxis.physimu = xnat2phy(rot, lin) | |
127 | - val = self._maxis.physimu | |
128 | - return val | |
141 | + # ------------ do | |
129 | 142 | |
130 | 143 | def _do_goto(self, *args, **kwargs): |
131 | 144 | operation = args[0].upper() |
132 | 145 | target_raw = self.database.query("target") |
133 | 146 | target = float(target_raw) |
134 | 147 | inc = self.val2inc(target) |
135 | - lim_inf = self._mount_params["LIM_INF"] | |
136 | - lim_sup = self._mount_params["LIM_SUP"] | |
148 | + lim_inf = self._focuser_params["LIM_INF"] | |
149 | + lim_sup = self._focuser_params["LIM_SUP"] | |
137 | 150 | if inc<lim_inf or inc>lim_sup: |
138 | 151 | texte = f"Operation {operation} cannot be done because the target asked is {inc} which is outside the limits {lim_inf} and {lim_sup}" |
139 | 152 | raise ComponentDetectorFocuserException(ComponentDetectorFocuserException.ERR_TARGET_OUTSIDE_LIMITS, texte) |
... | ... | @@ -144,11 +157,13 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
144 | 157 | self._maxis.simu_motion_start("SLEW", position=inc, velocity=velocity, frame='inc', drift=0) |
145 | 158 | self.database.query("motion_simu", self.MOTION_STATE_SLEWING) |
146 | 159 | # --- call the real |
160 | + result = None | |
147 | 161 | if self.real: |
148 | 162 | result = self._my_do(*args, **kwargs) |
149 | 163 | return result |
150 | 164 | |
151 | 165 | def _do_stop(self, *args, **kwargs): |
166 | + result = None | |
152 | 167 | operation = args[0].upper() |
153 | 168 | # --- make the simulation |
154 | 169 | self.log = f"start simulation of {operation}" |
... | ... | @@ -181,11 +196,11 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
181 | 196 | raise ComponentException(ComponentException.ERR_OPERATION_NOT_FOUND, msg) |
182 | 197 | # --- |
183 | 198 | if operation == "GOTO": |
184 | - result = self._do_goto(self, *args, **kwargs) | |
199 | + result = self._do_goto(*args, **kwargs) | |
185 | 200 | elif operation == "STOP": |
186 | - result = self._do_stop(self, *args, **kwargs) | |
201 | + result = self._do_stop(*args, **kwargs) | |
187 | 202 | elif operation == "COORD": |
188 | - result = self._do_coord(self, *args, **kwargs) | |
203 | + result = self._do_coord(*args, **kwargs) | |
189 | 204 | else: |
190 | 205 | # --- only call the real method |
191 | 206 | self.log = f"start operation {operation}" |
... | ... | @@ -197,15 +212,40 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
197 | 212 | value = None |
198 | 213 | return value |
199 | 214 | |
215 | + # ------------ get | |
216 | + | |
217 | + def _get_real_motions(self, *args, **kwargs): | |
218 | + # To be overriden | |
219 | + return self._get_simu_motions() | |
220 | + | |
221 | + def _get_simu_motions(self, *args, **kwargs): | |
222 | + self._maxis.simu_update_inc() | |
223 | + motion_simu = self._maxis.motion_state_simu | |
224 | + self._queue.put(f"motion_simu = {motion_simu}") | |
225 | + return motion_simu | |
226 | + | |
227 | + def _my_get_final(self, *args, **kwargs): | |
228 | + # This method should be overriden | |
229 | + pass | |
230 | + | |
200 | 231 | def _my_get(self, *args, **kwargs): |
201 | 232 | key = args[0] |
202 | 233 | if key == "motion_simu": |
203 | - self._maxis.simu_update_inc() | |
204 | - motion_simu = self._maxis.motion_state_simu | |
205 | - self._queue.put(f"motion_simu = {motion_simu}") | |
206 | - return motion_simu | |
234 | + motion = self._get_simu_motions() | |
235 | + return motion | |
236 | + elif key == "motion": | |
237 | + if self.real == True: | |
238 | + motion = self._get_real_motions() | |
239 | + else: | |
240 | + motion = self._get_simu_motions() | |
241 | + self._queue.put(f"motion = {motion}") | |
242 | + return motion | |
243 | + else: | |
244 | + self._my_get_final(*args, **kwargs) | |
207 | 245 | return None |
208 | 246 | |
247 | + # ------------ conversions physic <-> native | |
248 | + | |
209 | 249 | def nat2phy(self, rot, lin, **kwargs): |
210 | 250 | # For Motoraxis |
211 | 251 | # Arguments are imposed |
... | ... | @@ -219,63 +259,75 @@ class ComponentDetectorFocuser(ComponentDetectorFocuserException, Component, Gui |
219 | 259 | rot = lin * 360 / self._mm_per_motor_rev |
220 | 260 | return rot, lin |
221 | 261 | |
222 | - def _my_init_params(self, param_optionals: dict, mount_types: dict): | |
223 | - return param_optionals, mount_types | |
262 | + # ------------ conversions value <-> increment | |
224 | 263 | |
225 | - def init(self, *args, **kwargs): | |
226 | - # --- Dico of optional parameters for all axis_types | |
227 | - param_optionals = {} | |
228 | - param_optionals["MODEL"] = (str, "") | |
229 | - param_optionals["MANUFACTURER"] = (str, "") | |
230 | - param_optionals["SERIAL_NUMBER"] = (str, "") | |
231 | - param_optionals["REAL"] = (bool, False) | |
232 | - param_optionals["DESCRIPTION"] = (str, "No description.") | |
233 | - param_optionals["SITE"] = (Siteobs,"GPS 0 E 45 100") | |
234 | - param_optionals["LIM_INF"] = (float,0) | |
235 | - param_optionals["LIM_SUP"] = (float,100000) | |
236 | - param_optionals["LOGO_FILE"] = (str,"") | |
237 | - param_optionals["CLIENT_DATA"] = (any,None) | |
238 | - param_optionals["INC_PER_MOTOR_REV"] = (float, 1000.0) | |
239 | - param_optionals["MM_PER_MOTOR_REV"] = (float, 2.0) | |
240 | - # --- Dico of axis_types and their parameters | |
241 | - mount_types = {} | |
242 | - mount_types["Z"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Detector focuser"]} } | |
243 | - # --- Add params and types when inherited | |
244 | - param_optionals, mount_types = self._my_init_params(param_optionals, mount_types) | |
245 | - # --- Decode args and kwargs parameters | |
246 | - self._mount_params = self.decode_args_kwargs(0, mount_types, param_optionals, *args, **kwargs) | |
247 | - # === | |
248 | - self._mount_type = self._mount_params["SELECTED_ARG"] | |
249 | - self._name = self._mount_params["NAME"] | |
250 | - self._description = self._mount_params["DESCRIPTION"] | |
251 | - self._model = self._mount_params["MODEL"] | |
252 | - self._manufacturer = self._mount_params["MANUFACTURER"] | |
253 | - self._serial_number= self._mount_params["SERIAL_NUMBER"] | |
254 | - # === local | |
255 | - if str(type(self._mount_params["SITE"])) == "<class 'siteobs.Siteobs'>": | |
256 | - self.siteobs = self._mount_params["SITE"] | |
257 | - else: | |
258 | - self.siteobs = Home(self._mount_params["SITE"]) | |
259 | - # --- Update the database using the queue | |
260 | - param = {} | |
261 | - param["motion_simu"] = self.MOTION_STATE_UNKNOWN | |
262 | - param["motion_real"] = self.MOTION_STATE_UNKNOWN | |
263 | - param["unit"] = "inc" # inc, rot, lin, phy | |
264 | - param["target"] = 0 # unit | |
265 | - param["speed_slew"] = 100 # unit/s | |
266 | - self._queue.put(param) | |
267 | - # --- | |
268 | - self._inc_per_motor_rev = self._mount_params["INC_PER_MOTOR_REV"] | |
269 | - self._mm_per_motor_rev = self._mount_params["MM_PER_MOTOR_REV"] | |
270 | - # --- | |
271 | - self._my_init1(*args, **kwargs) | |
272 | - # --- Motor simulator | |
273 | - self._maxis = Motoraxis("LIN", name = "Focuser", inc_per_motor_rev=self._inc_per_motor_rev, inc0=0, senseinc=1, real=False, mm_per_motor_rev=self._mm_per_motor_rev) | |
274 | - self._maxis.define_phy = [self.nat2phy, self.phy2nat] | |
275 | - self._maxis.phy_unit = "inch" | |
276 | - # --- | |
277 | - self._my_init2(*args, **kwargs) | |
264 | + def val2inc(self, val:float): | |
265 | + """Conversion coder from unit to increment | |
278 | 266 | |
267 | + Args: | |
268 | + | |
269 | + val: Value in unit defined by the variable 'unit'. | |
270 | + | |
271 | + Returns: | |
272 | + | |
273 | + Increments. | |
274 | + | |
275 | + """ | |
276 | + unit = self._get("unit") | |
277 | + inc = val | |
278 | + if unit=="phy": | |
279 | + phy = val | |
280 | + xnat2phy, xphy2nat = self._maxis.define_phy | |
281 | + rot, lin = xphy2nat(phy) | |
282 | + elif unit=="lin": | |
283 | + lin = val | |
284 | + rot = self._maxis.lin2rot(lin, self._maxis.SAVE_NONE) | |
285 | + elif unit=="rot": | |
286 | + rot = val | |
287 | + if unit=="rot" or unit=="lin" or unit=="phy": | |
288 | + inc = self._maxis.rot2inc(rot, self._maxis.SAVE_NONE) | |
289 | + return inc | |
290 | + | |
291 | + def inc2val(self, inc:float): | |
292 | + """Conversion coder from increment to unit | |
293 | + | |
294 | + Args: | |
295 | + | |
296 | + inc: Increment. | |
297 | + | |
298 | + Returns: | |
299 | + | |
300 | + Value in unit defined by the variable 'unit'. | |
301 | + | |
302 | + """ | |
303 | + unit = self._get("unit") | |
304 | + val = inc | |
305 | + if unit=="rot" or unit=="lin" or unit=="phy": | |
306 | + rot = self._maxis.inc2rot(inc, self._maxis.SAVE_AS_SIMU) | |
307 | + val = rot | |
308 | + if unit=="lin" or unit=="phy": | |
309 | + lin = self._maxis.rot2lin(rot, self._maxis.SAVE_AS_SIMU) | |
310 | + val = lin | |
311 | + if unit=="phy": | |
312 | + xnat2phy, xphy2nat = self._maxis.define_phy | |
313 | + self._maxis.physimu = xnat2phy(rot, lin) | |
314 | + val = self._maxis.physimu | |
315 | + return val | |
316 | + | |
317 | + # ------------ conversion int -> string for human output | |
318 | + | |
319 | + def motion2string(self, motion:int, verb:bool= False): | |
320 | + if motion == self.MOTION_STATE_NOMOTION: | |
321 | + msg = "stoped" if verb else "stop" | |
322 | + elif motion == self.MOTION_STATE_SLEWING: | |
323 | + msg = "slewing" if verb else "slew" | |
324 | + elif motion == self.MOTION_STATE_DRIFTING: | |
325 | + msg = "drifting" if verb else "drift" | |
326 | + elif motion == self.MOTION_STATE_MOVING: | |
327 | + msg = "moving" if verb else "move" | |
328 | + else: | |
329 | + msg = "unknown" if verb else "unk" | |
330 | + return msg | |
279 | 331 | |
280 | 332 | # ##################################################################### |
281 | 333 | # ##################################################################### | ... | ... |
src/guitastro/component_mount_pointing.py
... | ... | @@ -104,12 +104,13 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
104 | 104 | SAVE_ALL = 3 |
105 | 105 | |
106 | 106 | # ===================================================================== |
107 | + # methods | |
107 | 108 | # ===================================================================== |
108 | - # General methods overriden | |
109 | - # ===================================================================== | |
110 | - # ===================================================================== | |
109 | + | |
110 | + # ------------ init | |
111 | 111 | |
112 | 112 | def _my_init_params(self, param_optionals: dict, mount_types: dict): |
113 | + # --- This method could be overriden | |
113 | 114 | return param_optionals, mount_types |
114 | 115 | |
115 | 116 | def init(self, *args, **kwargs): |
... | ... | @@ -252,6 +253,8 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
252 | 253 | param['pierside'] = Mountaxis.PIERSIDE_AUTO |
253 | 254 | self._queue.put(param) |
254 | 255 | |
256 | + # ------------ prop | |
257 | + | |
255 | 258 | def prop(self): |
256 | 259 | """Component property concrete method |
257 | 260 | """ |
... | ... | @@ -270,67 +273,7 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
270 | 273 | res['DO']['STOP'] = "Stop the mount motions" |
271 | 274 | return res |
272 | 275 | |
273 | - def travel_compute(self, inc_start:dict, inc_target:dict)->tuple: | |
274 | - """Compute the duration of a slewing from the current position to a target. | |
275 | - | |
276 | - Args: | |
277 | - | |
278 | - inc_start: Dictionary of increments at start position | |
279 | - inc_target: Dictionary of increments at end position | |
280 | - | |
281 | - Returns: | |
282 | - | |
283 | - delay_slew_maxi: A float value of the computed time travel | |
284 | - delay_slew: A dictionary of the computed time travel for each axis. The key is the axis symbol. | |
285 | - """ | |
286 | - # --- delta incs for the travel (signed incs) | |
287 | - delay_slew = {} | |
288 | - delay_slew_maxi = 0 | |
289 | - for axis in self.axis: | |
290 | - symbol = axis.symbol | |
291 | - if axis.real: | |
292 | - key = "real" | |
293 | - else: | |
294 | - key = "simu" | |
295 | - dinc = inc_target[key][symbol] - inc_start[key][symbol] | |
296 | - inc_per_sec = axis.slew_deg_per_sec * axis.inc_per_deg | |
297 | - delay_slew[symbol] = abs(dinc / inc_per_sec) | |
298 | - if delay_slew[symbol] > delay_slew_maxi: | |
299 | - delay_slew_maxi = delay_slew[symbol] | |
300 | - return delay_slew_maxi, delay_slew | |
301 | - | |
302 | - def _modpoi_apply(self, cel:dict , rot:dict, sense=1)->tuple: | |
303 | - """Corrige celestial coordinates of the pointing model. | |
304 | - | |
305 | - Args: | |
306 | - | |
307 | - cel: Input celestial coordinates | |
308 | - rot: Input rotation coordinates for pierside | |
309 | - sense: =1 for apparent to tel. -1 for tel to apparent. | |
310 | - | |
311 | - Returns: | |
312 | - | |
313 | - cel: Output celestial coordinates | |
314 | - | |
315 | - Mountaxis.PIERSIDE_POS1 for modpoi regular. | |
316 | - Mountaxis.PIERSIDE_POS2 for modpoi flipped. | |
317 | - """ | |
318 | - # --- | |
319 | - if self._apply_model: | |
320 | - simureal = 'real' if self.real else 'simu' | |
321 | - b, p = cel[simureal]['b'], cel[simureal]['p'] | |
322 | - pierside = rot[simureal]['pierside'] | |
323 | - db, dp = (0, 0) | |
324 | - if p<88 and p>-88: | |
325 | - if pierside == Mountaxis.PIERSIDE_POS1: | |
326 | - db, dp = self.modpoi_regular.coefs2ddata(b, p) | |
327 | - elif pierside == Mountaxis.PIERSIDE_POS2: | |
328 | - db, dp = self.modpoi_flipped.coefs2ddata(b, p) | |
329 | - if sense == 1 or sense == -1: | |
330 | - b = b - sense * db / 60.0 | |
331 | - p = p - sense * dp / 60.0 | |
332 | - cel['b'], cel['p'] = b, p | |
333 | - return cel | |
276 | + # ------------ do | |
334 | 277 | |
335 | 278 | def _do_goto(self, *args, **kwargs): |
336 | 279 | """Start a slewing motion to a target for GOTO and TRAJECTORY |
... | ... | @@ -588,6 +531,8 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
588 | 531 | value = None |
589 | 532 | return value |
590 | 533 | |
534 | + # ------------ set | |
535 | + | |
591 | 536 | def _my_set(self, *args, **kwargs): |
592 | 537 | key = args[0] |
593 | 538 | if key == "speed_slew": |
... | ... | @@ -616,41 +561,96 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
616 | 561 | k += 1 |
617 | 562 | return args, kwargs |
618 | 563 | |
564 | + # ------------ get | |
565 | + | |
566 | + def _get_real_motions(self, *args, **kwargs): | |
567 | + # To be overriden | |
568 | + return self._get_simu_motions() | |
569 | + | |
570 | + def _get_simu_motions(self, *args, **kwargs): | |
571 | + slewing = 0 | |
572 | + drifting = 0 | |
573 | + moving = 0 | |
574 | + param = {} # for motion_simu | |
575 | + param_dict = {} # for motions_simu | |
576 | + for axis in self.axis: | |
577 | + axis.simu_update_inc() | |
578 | + motion = axis.motion_state_simu | |
579 | + # --- | |
580 | + param_dict["motion_"+axis.axis_type+"_simu"] = motion | |
581 | + # --- | |
582 | + slewing += 1 if motion == self.MOTION_STATE_SLEWING else 0 | |
583 | + drifting += 1 if motion == self.MOTION_STATE_DRIFTING else 0 | |
584 | + moving += 1 if motion == self.MOTION_STATE_MOVING else 0 | |
585 | + # --- Global motion or dict | |
586 | + motion = self.MOTION_STATE_NOMOTION | |
587 | + if slewing>0: | |
588 | + motion = self.MOTION_STATE_SLEWING | |
589 | + elif moving>0: | |
590 | + motion = self.MOTION_STATE_MOVING | |
591 | + elif drifting>0: | |
592 | + motion = self.MOTION_STATE_DRIFTING | |
593 | + param["motion_simu"] = motion | |
594 | + return param_dict, motion | |
595 | + | |
596 | + def _my_get_final(self, *args, **kwargs): | |
597 | + # This method should be overriden | |
598 | + pass | |
599 | + | |
619 | 600 | def _my_get(self, *args, **kwargs): |
620 | 601 | key = args[0] |
602 | + param = {} | |
621 | 603 | if key == "motion_simu" or key == "motions_simu": |
622 | - # --- Motion of each axis | |
623 | - slewing = 0 | |
624 | - drifting = 0 | |
625 | - moving = 0 | |
626 | - param = {} # for motion_simu | |
627 | - param_dict = {} # for motions_simu | |
628 | - for axis in self.axis: | |
629 | - axis.simu_update_inc() | |
630 | - motion = axis.motion_state_simu | |
631 | - if key == "motions_simu": | |
632 | - param_dict["motion_"+axis.axis_type+"_simu"] = motion | |
633 | - else: | |
634 | - param["motion_"+axis.symbol+"_simu"] = motion | |
635 | - slewing += 1 if motion == self.MOTION_STATE_SLEWING else 0 | |
636 | - drifting += 1 if motion == self.MOTION_STATE_DRIFTING else 0 | |
637 | - moving += 1 if motion == self.MOTION_STATE_MOVING else 0 | |
638 | - # --- Global motion or dict | |
604 | + param_dict, motion = self._get_simu_motions() | |
639 | 605 | if key == "motions_simu": |
640 | 606 | motion = param_dict |
641 | 607 | param["motions_simu"] = param_dict |
642 | 608 | else: |
643 | - motion = self.MOTION_STATE_NOMOTION | |
644 | - if slewing>0: | |
645 | - motion = self.MOTION_STATE_SLEWING | |
646 | - elif moving>0: | |
647 | - motion = self.MOTION_STATE_MOVING | |
648 | - elif drifting>0: | |
649 | - motion = self.MOTION_STATE_DRIFTING | |
650 | 609 | param["motion_simu"] = motion |
651 | 610 | # --- Update database |
652 | 611 | self._queue.put(param) |
653 | 612 | return motion |
613 | + elif key == "motion" or key == "motions": | |
614 | + slewing = 0 | |
615 | + drifting = 0 | |
616 | + moving = 0 | |
617 | + # --- default values are from simu | |
618 | + param_dict, motion = self._get_simu_motions() | |
619 | + for axis in self.axis: | |
620 | + param_dict["motion_"+axis.axis_type] = param_dict["motion_"+axis.axis_type+"_simu"] | |
621 | + #print(f"A {param_dict=:}") | |
622 | + # --- replace values by real if possible | |
623 | + if self.real == True: | |
624 | + param_dict_real, motion_real = self._get_real_motions() | |
625 | + for axis in self.axis: | |
626 | + if axis.real: | |
627 | + param_dict["motion_"+axis.axis_type] = param_dict_real["motion_"+axis.axis_type+"_real"] | |
628 | + #print(f"B {param_dict=:}") | |
629 | + # --- mix values | |
630 | + for axis in self.axis: | |
631 | + # --- | |
632 | + motion = param_dict["motion_"+axis.axis_type] | |
633 | + # --- | |
634 | + slewing += 1 if motion == self.MOTION_STATE_SLEWING else 0 | |
635 | + drifting += 1 if motion == self.MOTION_STATE_DRIFTING else 0 | |
636 | + moving += 1 if motion == self.MOTION_STATE_MOVING else 0 | |
637 | + # --- Global motion or dict | |
638 | + motion = self.MOTION_STATE_NOMOTION | |
639 | + if slewing>0: | |
640 | + motion = self.MOTION_STATE_SLEWING | |
641 | + elif moving>0: | |
642 | + motion = self.MOTION_STATE_MOVING | |
643 | + elif drifting>0: | |
644 | + motion = self.MOTION_STATE_DRIFTING | |
645 | + param = {} | |
646 | + param["motion"] = motion | |
647 | + param["motions"] = param_dict | |
648 | + #print(f"C {param=:}") | |
649 | + # --- Update database | |
650 | + self._queue.put(param) | |
651 | + return motion | |
652 | + else: | |
653 | + self._my_get_final(*args, **kwargs) | |
654 | 654 | return None |
655 | 655 | |
656 | 656 | # ===================================================================== |
... | ... | @@ -676,49 +676,7 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
676 | 676 | # ===================================================================== |
677 | 677 | # ===================================================================== |
678 | 678 | |
679 | - def compute_pier_target(self, celb_target:float, pierside_start:int, imposed_side:int=Mountaxis.PIERSIDE_AUTO): | |
680 | - """ | |
681 | - Compute the predicted side of pier for the given ha,dec coordinates | |
682 | - | |
683 | - :param celb_target: Base angle target (unit is degrees). | |
684 | - :type celb_target: float | |
685 | - :param pierside_start: Current side | |
686 | - :type pierside_start: int | |
687 | - :param imposed_side: Side if it is imposed | |
688 | - :type imposed_side: int | |
689 | - | |
690 | - :returns: Pier side | |
691 | - :rtype: int | |
692 | - | |
693 | - * Pier side : Integer to indicate the back flip action: | |
694 | - | |
695 | - * PIERSIDE_POS1 (=1) pointing in normal position | |
696 | - * PIERSIDE_POS2 (=-1) pointing in back flip position | |
697 | - | |
698 | - """ | |
699 | - # --- compute the target pierside | |
700 | - if self._mount_params.get("CAN_REVERSE")==True: | |
701 | - lim_side_east = self._mount_params.get("LIME_REVERSE") ; # Tube west = PIERSIDE_POS1 = [-180 : lim_side_east] | |
702 | - lim_side_west = self._mount_params.get("LIMW_REVERSE") ; # Tube east = PIERSIDE_POS2 = [lim_side_west : +180] | |
703 | - if imposed_side == Mountaxis.PIERSIDE_AUTO: | |
704 | - if celb_target>lim_side_west and celb_target<lim_side_east: | |
705 | - # --- the target position is in the both possibilitiy range | |
706 | - pierside_target = pierside_start | |
707 | - else: | |
708 | - if celb_target>lim_side_east: | |
709 | - # --- the target is after the limit of side=PIERSIDE_POS1 | |
710 | - pierside_target = Mountaxis.PIERSIDE_POS2 | |
711 | - else: | |
712 | - # --- the target is before the limit of side=PIERSIDE_POS2 | |
713 | - pierside_target = Mountaxis.PIERSIDE_POS1 | |
714 | - else: | |
715 | - pierside_target = imposed_side | |
716 | - else: | |
717 | - if pierside_start == Mountaxis.PIERSIDE_AUTO: | |
718 | - pierside_target = Mountaxis.PIERSIDE_POS1 | |
719 | - else: | |
720 | - pierside_target = pierside_start | |
721 | - return pierside_target | |
679 | + # ------------ conversions target/astro <-> cel | |
722 | 680 | |
723 | 681 | def cel2astro(self, cel:dict, simureal:dict= {'b':'simu', 'p':'simu'})->dict: |
724 | 682 | jd = cel['header']['jd'] |
... | ... | @@ -789,23 +747,6 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
789 | 747 | raise ComponentMountPointingException(ComponentMountPointingException.ERR_MOUNT_TYPE_NOT_SUPPORTED, msg) |
790 | 748 | return cel |
791 | 749 | |
792 | - def _select_axis(self, symbol:str)->Mountaxis: | |
793 | - """Return the Mountaxis object selected from its rotation symbol | |
794 | - | |
795 | - Args: | |
796 | - | |
797 | - symbol: Rotation symbol must amongst 'b', 'p', 'r' | |
798 | - | |
799 | - Returns: | |
800 | - | |
801 | - Mountaxis object corresponding to the input symbol | |
802 | - """ | |
803 | - for axis in self.axis: | |
804 | - if axis == None: | |
805 | - continue | |
806 | - if symbol == axis.symbol: | |
807 | - return axis | |
808 | - | |
809 | 750 | def cel2inc(self, cel:dict, pierside:int, save:int)->tuple: |
810 | 751 | """Conversion from celestial mount coordinates (Cel) to rotation coordinates (Rot). |
811 | 752 | |
... | ... | @@ -881,6 +822,65 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
881 | 822 | # --- |
882 | 823 | return rot, inc |
883 | 824 | |
825 | + # ------------ conversion inc -> cel | |
826 | + | |
827 | + def inc2cel(self, incsimu:dict=None, save:int=Mountaxis.SAVE_NONE)->tuple: | |
828 | + """Conversion from reading increments to rotation and celestial coordinates (Rot, Cel). | |
829 | + | |
830 | + The complete conversion chain is: Inc->Rot->Cel-Astro | |
831 | + This method makes conversion Inc->Cel | |
832 | + | |
833 | + The Rot system is cardinaly oriented to avoid singularities. | |
834 | + | |
835 | + """ | |
836 | + inc = self.read_inc(incsimu) | |
837 | + #print(f"{inc=:}") | |
838 | + # --- rot dict | |
839 | + rot = {} | |
840 | + rot['header'] = {} | |
841 | + rot['header']['jd'] = inc['header']['jd'] | |
842 | + rot['simu'] = {} | |
843 | + rot['real'] = {} | |
844 | + # --- cel dict | |
845 | + cel = {} | |
846 | + cel['header'] = {} | |
847 | + cel['header']['jd'] = inc['header']['jd'] | |
848 | + cel['simu'] = {} | |
849 | + cel['real'] = {} | |
850 | + # --- save for simulations | |
851 | + if save==self.SAVE_ALL or save==self.SAVE_AS_SIMU: | |
852 | + savesimu = self.SAVE_AS_SIMU | |
853 | + else: | |
854 | + savesimu = self.SAVE_NONE | |
855 | + # --- save for real | |
856 | + if save==self.SAVE_ALL or save==self.SAVE_AS_REAL: | |
857 | + savereal = self.SAVE_AS_REAL | |
858 | + else: | |
859 | + savereal = self.SAVE_NONE | |
860 | + # --- loop over the mount axes | |
861 | + # --- First axe must be 'p' to determine the pierside | |
862 | + for axis in self.axes_first_p(): | |
863 | + axis_symbol = axis.symbol | |
864 | + # --- update for simulations | |
865 | + rotsimu, pierside = axis.inc2rot(inc['simu'][axis_symbol], savesimu) | |
866 | + if axis_symbol == 'p': | |
867 | + piersidesimu = pierside | |
868 | + celsimu = axis.rot2cel(rotsimu, piersidesimu, savesimu) | |
869 | + rot['simu'][axis_symbol] = rotsimu | |
870 | + cel['simu'][axis_symbol] = celsimu | |
871 | + # --- update for real | |
872 | + rotreal, pierside = axis.inc2rot(inc['real'][axis_symbol], savereal) | |
873 | + if axis_symbol == 'p': | |
874 | + piersidereal = pierside | |
875 | + celreal = axis.rot2cel(rotreal, piersidereal, savereal) | |
876 | + rot['real'][axis_symbol] = rotreal | |
877 | + cel['real'][axis_symbol] = celreal | |
878 | + rot['simu']['pierside'] = piersidesimu | |
879 | + rot['real']['pierside'] = piersidereal | |
880 | + return inc, rot, cel | |
881 | + | |
882 | + # ------------ read inc | |
883 | + | |
884 | 884 | def _my_read_inc(self, inc:dict)->dict: |
885 | 885 | """Read increments of encoders |
886 | 886 | |
... | ... | @@ -937,6 +937,8 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
937 | 937 | inc = self._my_read_inc(inc) |
938 | 938 | return inc |
939 | 939 | |
940 | + # ------------ get the axis object from its axis symbol | |
941 | + | |
940 | 942 | def symbol2axis(self, symbol:str)->object: |
941 | 943 | """Return the axis object from the symbol |
942 | 944 | |
... | ... | @@ -946,6 +948,8 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
946 | 948 | if symbol == axis.symbol: |
947 | 949 | return axis |
948 | 950 | |
951 | + # ------------ sort the object axis list to have 'p' first | |
952 | + | |
949 | 953 | def axes_first_p(self)->list: |
950 | 954 | """Return the axis objects list with symbol 'p' first |
951 | 955 | |
... | ... | @@ -961,60 +965,119 @@ class ComponentMountPointing(ComponentMountPointingException, Component, Guitast |
961 | 965 | axiss.append(axis) |
962 | 966 | return axiss |
963 | 967 | |
964 | - def inc2cel(self, incsimu:dict=None, save:int=Mountaxis.SAVE_NONE)->tuple: | |
965 | - """Conversion from reading increments to rotation and celestial coordinates (Rot, Cel). | |
968 | + # ------------ get the predicted pierside | |
966 | 969 | |
967 | - The complete conversion chain is: Inc->Rot->Cel-Astro | |
968 | - This method makes conversion Inc->Cel | |
970 | + def compute_pier_target(self, celb_target:float, pierside_start:int, imposed_side:int=Mountaxis.PIERSIDE_AUTO): | |
971 | + """ | |
972 | + Compute the predicted side of pier for the given ha,dec coordinates | |
969 | 973 | |
970 | - The Rot system is cardinaly oriented to avoid singularities. | |
974 | + :param celb_target: Base angle target (unit is degrees). | |
975 | + :type celb_target: float | |
976 | + :param pierside_start: Current side | |
977 | + :type pierside_start: int | |
978 | + :param imposed_side: Side if it is imposed | |
979 | + :type imposed_side: int | |
980 | + | |
981 | + :returns: Pier side | |
982 | + :rtype: int | |
983 | + | |
984 | + * Pier side : Integer to indicate the back flip action: | |
985 | + | |
986 | + * PIERSIDE_POS1 (=1) pointing in normal position | |
987 | + * PIERSIDE_POS2 (=-1) pointing in back flip position | |
971 | 988 | |
972 | 989 | """ |
973 | - inc = self.read_inc(incsimu) | |
974 | - #print(f"{inc=:}") | |
975 | - # --- rot dict | |
976 | - rot = {} | |
977 | - rot['header'] = {} | |
978 | - rot['header']['jd'] = inc['header']['jd'] | |
979 | - rot['simu'] = {} | |
980 | - rot['real'] = {} | |
981 | - # --- cel dict | |
982 | - cel = {} | |
983 | - cel['header'] = {} | |
984 | - cel['header']['jd'] = inc['header']['jd'] | |
985 | - cel['simu'] = {} | |
986 | - cel['real'] = {} | |
987 | - # --- save for simulations | |
988 | - if save==self.SAVE_ALL or save==self.SAVE_AS_SIMU: | |
989 | - savesimu = self.SAVE_AS_SIMU | |
990 | - else: | |
991 | - savesimu = self.SAVE_NONE | |
992 | - # --- save for real | |
993 | - if save==self.SAVE_ALL or save==self.SAVE_AS_REAL: | |
994 | - savereal = self.SAVE_AS_REAL | |
990 | + # --- compute the target pierside | |
991 | + if self._mount_params.get("CAN_REVERSE")==True: | |
992 | + lim_side_east = self._mount_params.get("LIME_REVERSE") ; # Tube west = PIERSIDE_POS1 = [-180 : lim_side_east] | |
993 | + lim_side_west = self._mount_params.get("LIMW_REVERSE") ; # Tube east = PIERSIDE_POS2 = [lim_side_west : +180] | |
994 | + if imposed_side == Mountaxis.PIERSIDE_AUTO: | |
995 | + if celb_target>lim_side_west and celb_target<lim_side_east: | |
996 | + # --- the target position is in the both possibilitiy range | |
997 | + pierside_target = pierside_start | |
998 | + else: | |
999 | + if celb_target>lim_side_east: | |
1000 | + # --- the target is after the limit of side=PIERSIDE_POS1 | |
1001 | + pierside_target = Mountaxis.PIERSIDE_POS2 | |
1002 | + else: | |
1003 | + # --- the target is before the limit of side=PIERSIDE_POS2 | |
1004 | + pierside_target = Mountaxis.PIERSIDE_POS1 | |
1005 | + else: | |
1006 | + pierside_target = imposed_side | |
995 | 1007 | else: |
996 | - savereal = self.SAVE_NONE | |
997 | - # --- loop over the mount axes | |
998 | - # --- First axe must be 'p' to determine the pierside | |
999 | - for axis in self.axes_first_p(): | |
1000 | - axis_symbol = axis.symbol | |
1001 | - # --- update for simulations | |
1002 | - rotsimu, pierside = axis.inc2rot(inc['simu'][axis_symbol], savesimu) | |
1003 | - if axis_symbol == 'p': | |
1004 | - piersidesimu = pierside | |
1005 | - celsimu = axis.rot2cel(rotsimu, piersidesimu, savesimu) | |
1006 | - rot['simu'][axis_symbol] = rotsimu | |
1007 | - cel['simu'][axis_symbol] = celsimu | |
1008 | - # --- update for real | |
1009 | - rotreal, pierside = axis.inc2rot(inc['real'][axis_symbol], savereal) | |
1010 | - if axis_symbol == 'p': | |
1011 | - piersidereal = pierside | |
1012 | - celreal = axis.rot2cel(rotreal, piersidereal, savereal) | |
1013 | - rot['real'][axis_symbol] = rotreal | |
1014 | - cel['real'][axis_symbol] = celreal | |
1015 | - rot['simu']['pierside'] = piersidesimu | |
1016 | - rot['real']['pierside'] = piersidereal | |
1017 | - return inc, rot, cel | |
1008 | + if pierside_start == Mountaxis.PIERSIDE_AUTO: | |
1009 | + pierside_target = Mountaxis.PIERSIDE_POS1 | |
1010 | + else: | |
1011 | + pierside_target = pierside_start | |
1012 | + return pierside_target | |
1013 | + | |
1014 | + # ------------ get the predicted time of slewing | |
1015 | + | |
1016 | + def travel_compute(self, inc_start:dict, inc_target:dict)->tuple: | |
1017 | + """Compute the duration of a slewing from the current position to a target. | |
1018 | + | |
1019 | + Args: | |
1020 | + | |
1021 | + inc_start: Dictionary of increments at start position | |
1022 | + inc_target: Dictionary of increments at end position | |
1023 | + | |
1024 | + Returns: | |
1025 | + | |
1026 | + delay_slew_maxi: A float value of the computed time travel | |
1027 | + delay_slew: A dictionary of the computed time travel for each axis. The key is the axis symbol. | |
1028 | + """ | |
1029 | + # --- delta incs for the travel (signed incs) | |
1030 | + delay_slew = {} | |
1031 | + delay_slew_maxi = 0 | |
1032 | + for axis in self.axis: | |
1033 | + symbol = axis.symbol | |
1034 | + if axis.real: | |
1035 | + key = "real" | |
1036 | + else: | |
1037 | + key = "simu" | |
1038 | + dinc = inc_target[key][symbol] - inc_start[key][symbol] | |
1039 | + inc_per_sec = axis.slew_deg_per_sec * axis.inc_per_deg | |
1040 | + delay_slew[symbol] = abs(dinc / inc_per_sec) | |
1041 | + if delay_slew[symbol] > delay_slew_maxi: | |
1042 | + delay_slew_maxi = delay_slew[symbol] | |
1043 | + return delay_slew_maxi, delay_slew | |
1044 | + | |
1045 | + # ------------ apply the pointing model | |
1046 | + | |
1047 | + def _modpoi_apply(self, cel:dict , rot:dict, sense=1)->tuple: | |
1048 | + """Corrige celestial coordinates of the pointing model. | |
1049 | + | |
1050 | + Args: | |
1051 | + | |
1052 | + cel: Input celestial coordinates | |
1053 | + rot: Input rotation coordinates for pierside | |
1054 | + sense: =1 for apparent to tel. -1 for tel to apparent. | |
1055 | + | |
1056 | + Returns: | |
1057 | + | |
1058 | + cel: Output celestial coordinates | |
1059 | + | |
1060 | + Mountaxis.PIERSIDE_POS1 for modpoi regular. | |
1061 | + Mountaxis.PIERSIDE_POS2 for modpoi flipped. | |
1062 | + """ | |
1063 | + # --- | |
1064 | + if self._apply_model: | |
1065 | + simureal = 'real' if self.real else 'simu' | |
1066 | + b, p = cel[simureal]['b'], cel[simureal]['p'] | |
1067 | + pierside = rot[simureal]['pierside'] | |
1068 | + db, dp = (0, 0) | |
1069 | + if p<88 and p>-88: | |
1070 | + if pierside == Mountaxis.PIERSIDE_POS1: | |
1071 | + db, dp = self.modpoi_regular.coefs2ddata(b, p) | |
1072 | + elif pierside == Mountaxis.PIERSIDE_POS2: | |
1073 | + db, dp = self.modpoi_flipped.coefs2ddata(b, p) | |
1074 | + if sense == 1 or sense == -1: | |
1075 | + b = b - sense * db / 60.0 | |
1076 | + p = p - sense * dp / 60.0 | |
1077 | + cel['b'], cel['p'] = b, p | |
1078 | + return cel | |
1079 | + | |
1080 | + # ------------ conversion int -> string for human output | |
1018 | 1081 | |
1019 | 1082 | def motion2string(self, motion:int, verb:bool= False): |
1020 | 1083 | if motion == self.MOTION_STATE_NOMOTION: | ... | ... |
src/guitastro/device.py
... | ... | @@ -5,6 +5,11 @@ import shlex |
5 | 5 | #from atexist import register |
6 | 6 | |
7 | 7 | try: |
8 | + from .siteobs import Siteobs | |
9 | +except: | |
10 | + from siteobs import Siteobs | |
11 | + | |
12 | +try: | |
8 | 13 | # guitastro is installed with setup.py |
9 | 14 | from guitastro import Communication, GuitastroException, GuitastroTools, Ephemeris |
10 | 15 | except: |
... | ... | @@ -262,6 +267,12 @@ class Device(DeviceException, GuitastroTools): |
262 | 267 | # ===================================================================== |
263 | 268 | # ===================================================================== |
264 | 269 | |
270 | + def _my_init_params(self, params_optional: dict, unit_types: dict): | |
271 | + return params_optional, unit_types | |
272 | + | |
273 | + def _my_init_final(self, *args, **kwargs): | |
274 | + pass | |
275 | + | |
265 | 276 | def __init__(self, *args, **kwargs): |
266 | 277 | """ |
267 | 278 | Conversion from Uniform Python object into protocol language |
... | ... | @@ -280,21 +291,27 @@ class Device(DeviceException, GuitastroTools): |
280 | 291 | params_optional["SERIAL_NUMBER"] = (str, "") |
281 | 292 | params_optional["REAL"] = (bool, False) |
282 | 293 | params_optional["DESCRIPTION"] = (str, "Just an abstract class. No component composed.") |
294 | + params_optional["SITE"] = (Siteobs, "GPS 0 E 45 100") | |
283 | 295 | # --- Dico of unit_types and their parameters |
284 | 296 | unit_types = {} |
285 | 297 | # --- unit choice |
286 | - unit_types["NOTHING"] = {"MANDATORY" : {"TRANSPORT":(str,"SERIAL")}, "OPTIONAL" : {"HOST":[str,"192.168.0.1"], "PORT":[int,1025]} } | |
298 | + unit_types["NOTHING"] = {"MANDATORY" : {"TRANSPORT":(str,"SERIAL")}, "OPTIONAL" : {"HOST":[str,"192.168.0.1"], "PORT":[int,1025], "BAUD":[int,115200]} } | |
299 | + # --- Add params and types when inherited | |
300 | + params_optional, unit_types = self._my_init_params(params_optional, unit_types) | |
287 | 301 | # --- Decode args and kwargs parameters |
288 | 302 | self._unit_params = self.decode_args_kwargs(0, unit_types, params_optional, *args, **kwargs) |
303 | + # === Observatory location for ephemeris | |
304 | + if str(type(self._unit_params["SITE"])) == "<class 'siteobs.Siteobs'>": | |
305 | + self.siteobs = self._unit_params["SITE"] | |
306 | + else: | |
307 | + self.siteobs = Siteobs(self._unit_params["SITE"]) | |
289 | 308 | # === |
290 | 309 | self.unit_type = self._unit_params["SELECTED_ARG"] |
291 | - # --- init ephemeris | |
292 | - eph = Ephemeris() | |
293 | - eph.set_home("148") | |
294 | 310 | # === Instanciate components of the device |
295 | 311 | # This is a composition of Component classes |
296 | 312 | self._comp = {} |
297 | 313 | # in a concrete class you have to add components here |
314 | + self._my_init_final(self, *args, **kwargs) | |
298 | 315 | |
299 | 316 | #@register |
300 | 317 | def __del__(self): | ... | ... |
src/guitastro/ephemeris.py
... | ... | @@ -91,7 +91,8 @@ class Ephemeris(EphemerisException, GuitastroTools): |
91 | 91 | target = "sun" |
92 | 92 | eph.set_home("guitalens") |
93 | 93 | date = "Now" |
94 | - ra, dec, equinox, epoch = eph.radec(target, unit_ra="H0.2", unit_dec="d+090.1") | |
94 | + ephem = eph.radec(target, unit_ra="H0.2", unit_dec="d+090.1") | |
95 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
95 | 96 | print(f"{name} ra={ra} dec={dec}") |
96 | 97 | |
97 | 98 | To get the drift: |
... | ... | @@ -99,16 +100,20 @@ class Ephemeris(EphemerisException, GuitastroTools): |
99 | 100 | target = "sun" |
100 | 101 | eph.set_home("guitalens") |
101 | 102 | date = "Now" |
102 | - ra, dec, equinox, epoch, dra, ddec = eph.radec_speed(target, unit_ra="H0.2", unit_dec="d+090.1") | |
103 | + ephem = eph.radec_speed(target, unit_ra="H0.2", unit_dec="d+090.1") | |
104 | + ra, dec, equinox, epoch, dra, ddec = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'], ephem['dra_equinox'], ephem['ddec_equinox'] | |
103 | 105 | print(f"{name} ra={ra} dec={dec} dra={dra} ddec={ddec}") |
104 | 106 | |
107 | + Note the output of the method radec is ephemeris dictionary. | |
108 | + | |
105 | 109 | Other examples of inputs: |
106 | 110 | |
107 | 111 | :: |
108 | 112 | |
109 | 113 | eph.set_home("GPS 2.567 E -21.456 1205") |
110 | 114 | date = "2022-01-31T22:34:15" |
111 | - ra, dec, equinox, epoch = eph.radec(target, unit_ra="H0.2", unit_dec="d+090.1") | |
115 | + ephem = eph.radec(target, unit_ra="H0.2", unit_dec="d+090.1") | |
116 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
112 | 117 | |
113 | 118 | The target is a major planet of the solar system (+ Moon and Sun): |
114 | 119 | |
... | ... | @@ -161,9 +166,11 @@ class Ephemeris(EphemerisException, GuitastroTools): |
161 | 166 | :: |
162 | 167 | |
163 | 168 | target = "CELESTRACK ISS" |
164 | - ra, dec, dra, ddec = eph.radec_speed(target, date="now") | |
169 | + ephem = eph.radec_speed(target, date="now") | |
170 | + ra, dec, equinox, epoch, dra, ddec = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'], ephem['dra_equinox'], ephem['ddec_equinox'] | |
165 | 171 | print(f"{name} ra={ra} dec={dec} dra={dra*3600:.3f} ddec={ddec*3600:.3f}") |
166 | 172 | |
173 | + | |
167 | 174 | The target is a name which can be solved by SGP4 and its TLE. |
168 | 175 | |
169 | 176 | :: |
... | ... | @@ -172,7 +179,8 @@ class Ephemeris(EphemerisException, GuitastroTools): |
172 | 179 | tle = f"TLE {name}\\n" |
173 | 180 | tle += "1 40544U 15017A 22029.59832018 -.00000064 00000+0 00000+0 0 9991\\n" |
174 | 181 | tle += "2 40544 56.7391 25.1641 0005059 269.6475 90.3121 1.70475734 42591\\n" |
175 | - ra, dec, equinox, epoch, dra, ddec = eph.radec_speed(tle) | |
182 | + ephem = eph.radec_speed(tle) | |
183 | + ra, dec, equinox, epoch, dra, ddec = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'], ephem['dra_equinox'], ephem['ddec_equinox'] | |
176 | 184 | print(f"{name} ra={ra:.6f} dec={dec:.6f} dra={dra*3600:.5f} ddec={ddec*3600:.5f}") |
177 | 185 | |
178 | 186 | """ |
... | ... | @@ -376,6 +384,9 @@ class Ephemeris(EphemerisException, GuitastroTools): |
376 | 384 | def gcnc_download(self, name): |
377 | 385 | """Download the gcnc files if needed. |
378 | 386 | From https://gcn.gsfc.nasa.gov/selected.html |
387 | + Valid only from 020305 to 230414B | |
388 | + TODO https://github.com/nasa-gcn/gcn-kafka-python for earliests. | |
389 | + or https://gcn.nasa.gov/docs/contributing | |
379 | 390 | """ |
380 | 391 | equinox = "J2000" |
381 | 392 | sra = "0" | ... | ... |
src/guitastro/maps.py
... | ... | @@ -1045,7 +1045,8 @@ class Maps(MapsException, GuitastroTools): |
1045 | 1045 | eph.set_home(config['home']) |
1046 | 1046 | odate = Date(config['date']) |
1047 | 1047 | jd = odate.jd() |
1048 | - ra, dec, equinox, epoch = eph.radec(target, date=jd) | |
1048 | + ephem = eph.radec(target, date=jd) | |
1049 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1049 | 1050 | coord_target = (1, Angle(ra), Angle(dec)) |
1050 | 1051 | coord = Coords() |
1051 | 1052 | coord.coords(coord_target) |
... | ... | @@ -1053,7 +1054,8 @@ class Maps(MapsException, GuitastroTools): |
1053 | 1054 | dec = math.radians(dec) |
1054 | 1055 | sindec = math.sin(dec) |
1055 | 1056 | cosdec = math.cos(dec) |
1056 | - ra_sun, dec_sun, equinox, epoch = eph.radec("sun", date=jd) | |
1057 | + ephem = eph.radec("sun", date=jd) | |
1058 | + ra_sun, dec_sun = ephem['ra_equinox'], ephem['dec_equinox'] | |
1057 | 1059 | coord_sun = (1, Angle(ra_sun), Angle(dec_sun)) |
1058 | 1060 | dist_sun = coord.great_circle_distance(coord_sun) |
1059 | 1061 | #print(f"coord={coord}") |
... | ... | @@ -1062,7 +1064,8 @@ class Maps(MapsException, GuitastroTools): |
1062 | 1064 | dec_sun = math.radians(dec_sun) |
1063 | 1065 | sindec_sun = math.sin(dec_sun) |
1064 | 1066 | cosdec_sun = math.cos(dec_sun) |
1065 | - ra_moon, dec_moon, equinox, epoch = eph.radec("moon", date=jd) | |
1067 | + ephem = eph.radec("moon", date=jd) | |
1068 | + ra_moon, dec_moon = ephem['ra_equinox'], ephem['dec_equinox'] | |
1066 | 1069 | coord_moon = (1, Angle(ra_moon), Angle(dec_moon)) |
1067 | 1070 | dist_moon = coord.great_circle_distance(coord_moon) |
1068 | 1071 | # --- ephems |
... | ... | @@ -1425,7 +1428,8 @@ class Maps(MapsException, GuitastroTools): |
1425 | 1428 | if key in config.keys(): |
1426 | 1429 | config[key] = val |
1427 | 1430 | eph = Ephemeris() |
1428 | - ra, dec, equinox, epoch = eph.radec(target) | |
1431 | + ephem = eph.radec(target) | |
1432 | + ra, dec = ephem['ra_equinox'], ephem['dec_equinox'] | |
1429 | 1433 | if config['date'] == "": |
1430 | 1434 | date = epoch |
1431 | 1435 | else: |
... | ... | @@ -1554,7 +1558,8 @@ if __name__ == "__main__": |
1554 | 1558 | name = "221120A" |
1555 | 1559 | target = f"GCNC {name}" |
1556 | 1560 | eph = Ephemeris() |
1557 | - ra, dec, equinox, epoch = eph.radec(target) | |
1561 | + ephem = eph.radec(target) | |
1562 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1558 | 1563 | date = epoch |
1559 | 1564 | title = f"Visibility (elevation) map of GRB {name} at {date}" |
1560 | 1565 | shortname = f"GRB {name}" |
... | ... | @@ -1575,7 +1580,8 @@ if __name__ == "__main__": |
1575 | 1580 | name = "221009A" |
1576 | 1581 | target = "RADEC 288.25375d +19.80947" |
1577 | 1582 | eph = Ephemeris() |
1578 | - ra, dec, equinox, epoch = eph.radec(target) | |
1583 | + ephem = eph.radec(target) | |
1584 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1579 | 1585 | date = "2022-10-09T14:10:17.99" |
1580 | 1586 | title = f"Visibility (elevation) map of GRB {name} at {date}" |
1581 | 1587 | shortname = f"GRB {name}" |
... | ... | @@ -1612,7 +1618,8 @@ if __name__ == "__main__": |
1612 | 1618 | name = "221120A" |
1613 | 1619 | target = f"GCNC {name}" |
1614 | 1620 | eph = Ephemeris() |
1615 | - ra, dec, equinox, epoch = eph.radec(target) | |
1621 | + ephem = eph.radec(target) | |
1622 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1616 | 1623 | date = "2022-04-12T20:44:00" # "2022-04-11T07:20:00" epoch |
1617 | 1624 | title = f"Visibility (elevation) map of GRB {name} at {date}" |
1618 | 1625 | output_file = os.path.join(path_products, f"map_GRB{name}.png") |
... | ... | @@ -1655,7 +1662,8 @@ if __name__ == "__main__": |
1655 | 1662 | eph = Ephemeris() |
1656 | 1663 | eph.set_home(home) |
1657 | 1664 | name = "jupiter" |
1658 | - ra, dec, equinox, epoch = eph.radec(name, date=date, unit_ra="H0.2", unit_dec="d+090.1") | |
1665 | + ephem = eph.radec(name, date=date, unit_ra="H0.2", unit_dec="d+090.1") | |
1666 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1659 | 1667 | ang.angle(ra) |
1660 | 1668 | target["ra"] = ang.deg() |
1661 | 1669 | ang.angle(dec) |
... | ... | @@ -1664,7 +1672,8 @@ if __name__ == "__main__": |
1664 | 1672 | target["color"] = "#00FF00" |
1665 | 1673 | targets.append(target.copy()) |
1666 | 1674 | name = "saturn" |
1667 | - ra, dec, equinox, epoch = eph.radec(name, date=date, unit_ra="H0.2", unit_dec="d+090.1") | |
1675 | + ephem = eph.radec(name, date=date, unit_ra="H0.2", unit_dec="d+090.1") | |
1676 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1668 | 1677 | ang.angle(ra) |
1669 | 1678 | target["ra"] = ang.deg() |
1670 | 1679 | ang.angle(dec) |
... | ... | @@ -1680,10 +1689,10 @@ if __name__ == "__main__": |
1680 | 1689 | """ This example generate a web page for a GRB |
1681 | 1690 | """ |
1682 | 1691 | map1 = Maps() |
1683 | - name = "221009A" | |
1684 | - target = f"GCNC {name}" | |
1685 | - #target = "RADEC 85.5770d +14.03340d" | |
1686 | - date = "2022-10-09T13:16:59" | |
1692 | + name = "230414B" | |
1693 | + #target = f"GCNC {name}" | |
1694 | + target = "RADEC 181.09439d 53.15428d" | |
1695 | + date = "2023-04-14T16:14:21" | |
1687 | 1696 | #date = "" |
1688 | 1697 | shortname = f"GRB {name}" |
1689 | 1698 | siteobss = map1.get_siteobss("GRANDMA") |
... | ... | @@ -1703,6 +1712,9 @@ if __name__ == "__main__": |
1703 | 1712 | name = "DART" |
1704 | 1713 | target = "RADEC 03h23m20.8s -33d26m33s" |
1705 | 1714 | date = "2022-09-26T23:14:38" |
1715 | + name = "GRB 230414B" | |
1716 | + target = "RADEC 12h04m17s +53d07m36s" | |
1717 | + date = "2023-04-14T16:14:21" | |
1706 | 1718 | shortname = f"Impact of {name}" |
1707 | 1719 | siteobss = [] |
1708 | 1720 | siteobs = {} |
... | ... | @@ -1739,13 +1751,15 @@ if __name__ == "__main__": |
1739 | 1751 | name = "221024A" |
1740 | 1752 | target = "RADEC 169.7413d +33.0419" |
1741 | 1753 | eph = Ephemeris() |
1742 | - ra, dec, equinox, epoch = eph.radec(target) | |
1754 | + ephem = eph.radec(target) | |
1755 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1743 | 1756 | date = "2022-10-24T16:00:27.12" |
1744 | 1757 | else: |
1745 | 1758 | name = "220408A" |
1746 | 1759 | target = f"GCNC {name}" |
1747 | 1760 | eph = Ephemeris() |
1748 | - ra, dec, equinox, epoch = eph.radec(target) | |
1761 | + ephem = eph.radec(target) | |
1762 | + ra, dec, equinox, epoch = ephem['ra_equinox'], ephem['dec_equinox'], ephem['header']['equinox'], ephem['jd'] | |
1749 | 1763 | date = epoch |
1750 | 1764 | title = f"Visibility (elevation) map of GRB {name} at {date}" |
1751 | 1765 | output_file = os.path.join(path_products, f"map_GRB{name}.pdf") | ... | ... |