18
18
19
19
__all__ = ['Ephemeris' ]
20
20
21
- _default_t0 = 0
22
- _default_period = 1
23
- _default_dpdt = 0
21
+ _default_t0 = 0.0
22
+ _default_period = 1.0
23
+ _default_dpdt = 0.0
24
+ _default_wrap_at = 1.0
24
25
25
26
26
27
@tray_registry ('ephemeris' , label = "Ephemeris" )
@@ -39,6 +40,8 @@ class Ephemeris(PluginTemplateMixin, DatasetSelectMixin):
39
40
Period of the ephemeris, defined at ``t0``.
40
41
* :attr:`dpdt`:
41
42
First derivative of the period of the ephemeris.
43
+ * :attr:`wrap_at`:
44
+ Phase at which to wrap (phased data will encompass the range 1-wrap_at to wrap_at).
42
45
* :meth:`ephemeris`
43
46
* :meth:`ephemerides`
44
47
* :meth:`update_ephemeris`
@@ -68,6 +71,7 @@ class Ephemeris(PluginTemplateMixin, DatasetSelectMixin):
68
71
period_step = Float (0.1 ).tag (sync = True )
69
72
dpdt = FloatHandleEmpty (_default_dpdt ).tag (sync = True )
70
73
dpdt_step = Float (0.1 ).tag (sync = True )
74
+ wrap_at = FloatHandleEmpty (_default_wrap_at ).tag (sync = True )
71
75
72
76
# PERIOD FINDING
73
77
method_items = List ().tag (sync = True )
@@ -83,6 +87,7 @@ def __init__(self, *args, **kwargs):
83
87
84
88
self ._ignore_ephem_change = False
85
89
self ._ephemerides = {}
90
+ self ._prev_wrap_at = _default_wrap_at
86
91
87
92
self .component = EditableSelectPluginComponent (self ,
88
93
mode = 'component_mode' ,
@@ -108,7 +113,7 @@ def __init__(self, *args, **kwargs):
108
113
109
114
@property
110
115
def user_api (self ):
111
- expose = ['component' , 'period' , 'dpdt' , 't0' ,
116
+ expose = ['component' , 'period' , 'dpdt' , 't0' , 'wrap_at' ,
112
117
'ephemeris' , 'ephemerides' ,
113
118
'update_ephemeris' , 'create_phase_viewer' ,
114
119
'add_component' , 'remove_component' , 'rename_component' ,
@@ -136,6 +141,12 @@ def phase_viewer_ids(self):
136
141
def phase_viewer_id (self ):
137
142
return self ._phase_viewer_id (self .component_selected )
138
143
144
+ @property
145
+ def phase_viewer (self ):
146
+ if not self .phase_viewer_exists :
147
+ return None
148
+ return self .app .get_viewer (self .phase_viewer_id )
149
+
139
150
@property
140
151
def ephemerides (self ):
141
152
return self ._ephemerides
@@ -150,17 +161,19 @@ def _times_to_phases_callable(self, component):
150
161
t0 = self .t0
151
162
period = self .period
152
163
dpdt = self .dpdt
164
+ wrap_at = self .wrap_at
153
165
else :
154
166
ephem = self .ephemerides .get (component , {})
155
167
t0 = ephem .get ('t0' , _default_t0 )
156
168
period = ephem .get ('period' , _default_period )
157
169
dpdt = ephem .get ('dpdt' , _default_dpdt )
170
+ wrap_at = ephem .get ('wrap_at' , _default_wrap_at )
158
171
159
172
def _callable (times ):
160
173
if dpdt != 0 :
161
- return np .mod (1. / dpdt * np .log (1 + dpdt / period * (times - t0 )), 1.0 ) # noqa
174
+ return np .mod (1. / dpdt * np .log (1 + dpdt / period * (times - t0 )) + ( 1 - wrap_at ) , 1.0 ) - ( 1 - wrap_at ) # noqa
162
175
else :
163
- return np .mod ((times - t0 )/ period , 1.0 )
176
+ return np .mod ((times - t0 )/ period + ( 1 - wrap_at ) , 1.0 ) - ( 1 - wrap_at )
164
177
165
178
return _callable
166
179
@@ -242,7 +255,8 @@ def create_phase_viewer(self):
242
255
if self .phase_comp_lbl not in [comp .label for comp in dc [0 ].components ]:
243
256
self .update_ephemeris () # calls _update_all_phase_arrays
244
257
245
- if not self .phase_viewer_exists :
258
+ create_phase_viewer = not self .phase_viewer_exists
259
+ if create_phase_viewer :
246
260
# TODO: stack horizontally by default?
247
261
self .app ._on_new_viewer (NewViewerMessage (PhaseScatterView , data = None , sender = self .app ),
248
262
vid = phase_viewer_id , name = phase_viewer_id )
@@ -254,6 +268,8 @@ def create_phase_viewer(self):
254
268
self .app .set_data_visibility (phase_viewer_id , data .label , visible == 'visible' )
255
269
256
270
pv = self .app .get_viewer (phase_viewer_id )
271
+ if create_phase_viewer :
272
+ pv .state .x_min , pv .state .x_max = (self .wrap_at - 1 , self .wrap_at )
257
273
pv .state .x_att = self .phase_cids [self .component_selected ]
258
274
return pv
259
275
@@ -314,17 +330,18 @@ def _change_component(self, *args):
314
330
self .t0 = ephem .get ('t0' , self .t0 )
315
331
self .period = ephem .get ('period' , self .period )
316
332
self .dpdt = ephem .get ('dpdt' , self .dpdt )
333
+ self .wrap_at = ephem .get ('wrap_at' , self .wrap_at )
317
334
318
335
# if this is a new component, update those default values back to the dictionary
319
- self .update_ephemeris (t0 = self .t0 , period = self .period , dpdt = self .dpdt )
336
+ self .update_ephemeris (t0 = self .t0 , period = self .period , dpdt = self .dpdt , wrap_at = self . wrap_at )
320
337
self ._ignore_ephem_change = False
321
338
if ephem :
322
339
# if there were any changes applied by accessing the dictionary,
323
340
# then we need to update phasing, etc (since we set _ignore_ephem_change)
324
341
# otherwise, this is a new component and there is no need.
325
342
self ._ephem_traitlet_changed ()
326
343
327
- def update_ephemeris (self , component = None , t0 = None , period = None , dpdt = None ):
344
+ def update_ephemeris (self , component = None , t0 = None , period = None , dpdt = None , wrap_at = None ):
328
345
"""
329
346
Update the ephemeris for a given component.
330
347
@@ -337,7 +354,9 @@ def update_ephemeris(self, component=None, t0=None, period=None, dpdt=None):
337
354
period : float, optional
338
355
value of period to replace
339
356
dpdt : float, optional
340
- value of period to replace
357
+ value of dpdt to replace
358
+ wrap_at : float, optional
359
+ value of wrap_at to replace
341
360
342
361
Returns
343
362
-------
@@ -350,7 +369,7 @@ def update_ephemeris(self, component=None, t0=None, period=None, dpdt=None):
350
369
raise ValueError (f"component must be one of { self .component .choices } " )
351
370
352
371
existing_ephem = self ._ephemerides .get (component , {})
353
- for name , value in {'t0' : t0 , 'period' : period , 'dpdt' : dpdt }.items ():
372
+ for name , value in {'t0' : t0 , 'period' : period , 'dpdt' : dpdt , 'wrap_at' : wrap_at }.items ():
354
373
if value is not None :
355
374
existing_ephem [name ] = value
356
375
if component == self .component_selected :
@@ -360,11 +379,11 @@ def update_ephemeris(self, component=None, t0=None, period=None, dpdt=None):
360
379
self ._update_all_phase_arrays (component = component )
361
380
return existing_ephem
362
381
363
- @observe ('period' , 'dpdt' , 't0' )
382
+ @observe ('period' , 'dpdt' , 't0' , 'wrap_at' )
364
383
def _ephem_traitlet_changed (self , event = {}):
365
384
if self ._ignore_ephem_change :
366
385
return
367
- for value in (self .period , self .dpdt , self .t0 ):
386
+ for value in (self .period , self .dpdt , self .t0 , self . wrap_at ):
368
387
if not isinstance (value , (int , float )):
369
388
return
370
389
if self .period <= 0 :
@@ -383,6 +402,17 @@ def round_to_1(x):
383
402
else :
384
403
self ._update_all_phase_arrays (component = self .component_selected )
385
404
405
+ # update zoom-limits if wrap_at was changed
406
+ if event .get ('name' ) == 'wrap_at' :
407
+ old = event .get ('old' ) if event .get ('old' ) != '' else self ._prev_wrap_at
408
+ if event .get ('new' ) != '' :
409
+ pvs = self .phase_viewer .state
410
+ delta_phase = event .get ('new' ) - old
411
+ pvs .x_min , pvs .x_max = pvs .x_min + delta_phase , pvs .x_max + delta_phase
412
+ # we need to cache the old value since it could become a string
413
+ # if the widget is cleared
414
+ self ._prev_wrap_at = event .get ('new' )
415
+
386
416
# update step-sizes
387
417
self .period_step = round_to_1 (self .period / 5000 )
388
418
self .dpdt_step = max (round_to_1 (abs (self .period * self .dpdt )/ 1000 ) if self .dpdt != 0 else 0 ,
0 commit comments