77import numpy as np
88from astropy .constants import c
99from astropy .io import fits
10- from casatools .table import table
10+ from astropy .time import Time
11+ from casacore .tables import table
1112from numpy .exceptions import AxisError
1213from numpy .typing import ArrayLike
1314
@@ -89,6 +90,7 @@ def __init__(
8990 self ,
9091 u_meter : np .ndarray ,
9192 v_meter : np .ndarray ,
93+ times : np .ndarray ,
9294 img_size : int ,
9395 fov : float ,
9496 ref_frequency : float ,
@@ -107,6 +109,8 @@ def __init__(
107109 The u coordinates in meter.
108110 v_meter : numpy.ndarray
109111 The v coordinates in meter.
112+ times : numpy.ndarray
113+ The times (in MJD) at which the visibilities were measured.
110114 img_size : int
111115 The size of the image in pixels.
112116 fov : float
@@ -131,6 +135,8 @@ def __init__(
131135 self .u_meter = u_meter
132136 self .v_meter = v_meter
133137
138+ self .times = Time (np .tile (times , reps = self .frequencies .size ), format = "mjd" )
139+
134140 self .img_size = img_size
135141
136142 self .fov = np .deg2rad (fov / 3600 ) # convert from asec to rad
@@ -281,6 +287,8 @@ def from_pyvisgen(
281287 u_meter = vis_data .u
282288 v_meter = vis_data .v
283289
290+ times = obs .baselines .time / 3600 / 24
291+
284292 vis_data = vis_data .get_values ()
285293
286294 if vis_data .ndim != 7 :
@@ -295,6 +303,7 @@ def from_pyvisgen(
295303 cls = cls (
296304 u_meter = u_meter .cpu ().numpy (),
297305 v_meter = v_meter .cpu ().numpy (),
306+ times = times ,
298307 img_size = img_size ,
299308 fov = fov ,
300309 ref_frequency = obs .ref_frequency .cpu ().numpy (),
@@ -363,12 +372,6 @@ def from_fits(
363372 uv_colnames = dict (u = None , v = None )
364373
365374 path = Path (path )
366-
367- if not path .is_file () or path .suffix .lower () != ".fits" :
368- raise FileNotFoundError (
369- f"The file { path } is not valid! You have to select a valid .fits file!"
370- )
371-
372375 file = fits .open (path )
373376
374377 data = file [0 ].data .T
@@ -391,6 +394,8 @@ def from_fits(
391394 u_meter = data [uv_colnames [0 ]].T * c .value
392395 v_meter = data [uv_colnames [1 ]].T * c .value
393396
397+ times = Time (data ["DATE" ], format = "jd" ).mjd
398+
394399 vis = file [0 ].data ["DATA" ]
395400 stokes_i = (
396401 (vis [..., 0 , 0 ] + 1j * vis [..., 0 , 1 ])
@@ -400,6 +405,7 @@ def from_fits(
400405 cls = cls (
401406 u_meter = u_meter ,
402407 v_meter = v_meter ,
408+ times = times ,
403409 img_size = img_size ,
404410 fov = fov ,
405411 ref_frequency = file [0 ].header ["CRVAL4" ],
@@ -454,54 +460,52 @@ def from_ms(
454460 f"This measurement set does not exist under the path { path } "
455461 )
456462
457- tab = table (str (path ))
463+ main_tab = table (str (path ), ack = False )
464+ spectral_tab = table (str (path / "SPECTRAL_WINDOW" ), ack = False )
458465
459466 data_colname = "DATA" if not use_calibrated else "MODEL_DATA"
460467
461468 if desc_id is not None :
462- mask = tab .getcol ("DATA_DESC_ID" ) == desc_id
469+ mask = main_tab .getcol ("DATA_DESC_ID" ) == desc_id
463470 mask_idx = np .argwhere (mask ).ravel ()
464471
465- tab_subset = tab .selectrows (rownrs = mask_idx )
472+ main_tab = main_tab .selectrows (rownrs = mask_idx )
466473
467- data = tab_subset .getcol (data_colname )
468- uv = tab_subset .getcol ("UVW" )[:2 ]
474+ data = main_tab .getcol (data_colname )
475+ uv = main_tab .getcol ("UVW" )[:, :2 ]
476+ times = main_tab .getcol ("TIME" )
469477
470- else :
471- mask = np .ones_like (tab .getcol ("DATA_DESC_ID" )).astype (bool )
472- data = tab .getcol (data_colname )
473- uv = tab .getcol ("UVW" )[:2 ]
474-
475- spectral_tab = table (str (path / "SPECTRAL_WINDOW" ))
476-
477- if desc_id is not None :
478478 ref_frequency = spectral_tab .getcell ("REF_FREQUENCY" , desc_id )
479- else :
480- ref_frequency = spectral_tab .getcell ("REF_FREQUENCY" , 0 )
481-
482- if desc_id is not None :
483479 frequency_offsets = (
484480 spectral_tab .getcell ("CHAN_FREQ" , desc_id ) - ref_frequency
485481 )
482+
486483 else :
484+ mask = np .ones_like (main_tab .getcol ("DATA_DESC_ID" )).astype (bool )
485+ data = main_tab .getcol (data_colname )
486+ uv = main_tab .getcol ("UVW" )[:, :2 ]
487+ times = main_tab .getcol ("TIME" )
488+
489+ ref_frequency = spectral_tab .getcell ("REF_FREQUENCY" , 0 )
487490 frequency_offsets = spectral_tab .getcell ("CHAN_FREQ" , 0 ) - ref_frequency
488491
489492 if filter_flagged :
490- flag_mask = tab .getcol ("FLAG" )[..., mask ]
491- flag_mask = flag_mask .reshape ((- 1 , flag_mask .shape [- 1 ])).astype (np .uint8 )
493+ flag_mask = main_tab .getcol ("FLAG" )
494+ flag_mask = flag_mask .reshape ((- 1 , flag_mask .shape [0 ])).astype (np .uint8 )
492495 flag_mask = np .prod (flag_mask , axis = 0 )
493496
494497 flag_mask = np .logical_not (flag_mask .astype (bool ))
498+
495499 else :
496500 flag_mask = np .ones (uv .shape [- 1 ]).astype (bool )
497501
498- uv = uv [..., flag_mask ]
499- data = data [..., flag_mask ]
502+ uv = uv [flag_mask ]
503+ data = data [flag_mask ]
500504
501- u_meter = uv [0 ]
502- v_meter = uv [1 ]
505+ u_meter = uv [:, 0 ]
506+ v_meter = uv [:, 1 ]
503507
504- stokes_i = data [0 ] + data [1 ]
508+ stokes_i = data [..., 0 ] + data [..., 1 ]
505509
506510 # FIXME: probably some kind of difference in normalization.
507511 # Factor 0.5 fixes this for now. Has to be investigated.
@@ -510,6 +514,7 @@ def from_ms(
510514 cls = cls (
511515 u_meter = u_meter ,
512516 v_meter = v_meter ,
517+ times = Time (times [flag_mask ] / 3600 / 24 , format = "mjd" ).mjd ,
513518 img_size = img_size ,
514519 fov = fov ,
515520 ref_frequency = ref_frequency ,
@@ -525,12 +530,28 @@ def plot_ungridded_uv(self, **kwargs):
525530
526531 Parameters
527532 ----------
533+ gridder : pyvisgrid.Gridder
534+ The gridder from which to take the (u,v) coordinates.
528535 mode : str, optional
529536 The mode specifying the scale of the (u,v) coordinates.
530537 This can be either ``wave``, meaning the coordinates are
531538 plotted in units of the reference wavelength, or ``meter``,
532539 meaning the (u,v) coordinates will be plotted in meter.
533540 Default is ``wave``.
541+ show_times : bool, optional
542+ Whether to show the timestamps of the measured visibilities
543+ as a colormap. Default is ``True``.
544+ use_relative_time : bool, optional
545+ Whether to show the times relative to the timestamp of the
546+ first measurement in hours.
547+ Default is ``True``.
548+ times_cmap: str | matplotlib.colors.Colormap, optional
549+ The colormap to be used for the time component of the plot.
550+ Default is ``'inferno'``.
551+ colorbar_shrink: float, optional
552+ The shrink parameter of the colorbar. This can be needed if the plot is
553+ included as a subplot to adjust the size of the colorbar.
554+ Default is ``1``, meaning original scale.
534555 marker_size : float | None, optional
535556 The size of the scatter markers in points**2.
536557 Default is ``None``, meaning the default value supplied by
0 commit comments