#########################################################################
#
#  task_plotbandpass.py
#
#  Developed at the NAASC, this is a generic task to display CASA Tsys and
#  bandpass solution tables with options to overlay them in various
#  combinations, and/or with an atmospheric transmission or sky temperature
#  model.  It works with both the 'new' (casa 3.4) and 'old' calibration
#  table formats, and allows for mixed mode spws (e.g. TDM and FDM for ALMA).
#  It uses the new msmd tool to access the information about an ms.
#
#  Todd R. Hunter  February 2013
#
# To test:  see plotbandpass_regression.py
#

import inspect
import math
import os
import re  # used for testing if a string is a float
import time
import copy

import matplotlib.transforms
import numpy as np
import pylab as pb
from casatasks import casalog
from casatasks.private import sdutil, simutil
from casatools import (atmosphere, ctsys, measures, ms, msmetadata, quanta,
                       table)
from matplotlib.ticker import (FormatStrFormatter, MultipleLocator,
                               ScalarFormatter)
from six.moves import input, range

# CAS-13722, CAS-13385
import warnings
import matplotlib.cbook
#warnings.filterwarnings("ignore",category=matplotlib.cbook.MatplotlibDeprecationWarning)

##------------------------------------------------------------------------------------------------------
##--- Increment the micro version number with each update to task_plotbandpass.py, adjust the major  ---
##--- and minor version numbers to match the analysisUtils version that is last synced with...       ---
##------------------------------------------------------------------------------------------------------
TASK_PLOTBANDPASS_REVISION_STRING = "2.20.0"                       #### incremented from 2.3.0 in 2024Aug
##------------------------------------------------------------------------------------------------------
##--- Update this version string whenever task_plotbandpass.py is synced with plotbandpass3.py from  ---
##--- Todd's analysisUtils. This will allow for tracking efforts to keep the versions synced.        ---
##------------------------------------------------------------------------------------------------------
PLOTBANDPASS_REVISION_STRING = "Id: plotbandpass3.py,v 2.20 2024/09/04 16:00:03 thunter Exp"

TOP_MARGIN  = 0.25   # Used if showatm=T or showtksy=T
BOTTOM_MARGIN = 0.25 # Used if showfdm=T
MAX_ATM_CALC_CHANNELS = 512

markeredgewidth = 0.0
maxAltitude = 60  # for ozone, in km, this is the default value of the parameter in the analysisUtils version

# This is a color sequence found online which has distinguishable colors
overlayColorsSequence = [
      [0.00,  0.00,  0.00],
      [0.00,  0.00,  1.00],
      [0.00,  0.50,  0.00],
      [1.00,  0.00,  0.00],
      [0.00,  0.75,  0.75],
#      [0.75,  0.00,  0.75], # magenta, same as atmcolor
      [0.25,  0.25,  0.25],
      [0.75,  0.25,  0.25],
      [0.95,  0.95,  0.00],
      [0.25,  0.25,  0.75],
#      [0.75,  0.75,  0.75],  this color is invisible for some reason
      [0.00,  1.00,  0.00],
      [0.76,  0.57,  0.17],
      [0.54,  0.63,  0.22],
      [0.34,  0.57,  0.92],
      [1.00,  0.10,  0.60],
#      [0.88,  0.75,  0.73],  invisible
      [0.10,  0.49,  0.47],
      [0.66,  0.34,  0.65],
      [0.99,  0.41,  0.23]]
overlayColorsList = overlayColorsSequence.copy()
overlayColorsList += overlayColorsList + overlayColorsList # 17*3 = 34 total color entries
overlayColorsList += overlayColorsList + overlayColorsList # try to support antenna,time  34*3 =102
overlayColorsList += overlayColorsList + overlayColorsList # try to support antenna,time  102*3=306
overlayColorsList += overlayColorsList + overlayColorsList # try to support antenna,time  306*3=918
overlayColorsList += overlayColorsList + overlayColorsList # try to support antenna,time  918*3=2754 entries

# Enumeration to keep track of plot pages
PAGE_ANT = 0
PAGE_SPW = 1
PAGE_TIME = 2
PAGE_AP = 3

# Used to parse command line arguments
myValidCharacterList = ['~', ',', ' ', '*',] + [str(m) for m in range(10)]
myValidCharacterListWithBang = ['~', ',', ' ', '*', '!',] + [str(m) for m in range(10)]
LARGE_POSITIVE = +1e20
LARGE_NEGATIVE = -1e20
maxAntennaNamesAcrossTheTop = 17
maxTimesAcrossTheTop = 13 # 17 for HH:MM, reduced by 1 below for subplot=11
antennaVerticalSpacing = 0.018 # 0.016
antennaHorizontalSpacing = 0.05
xstartTitle = 0.07
ystartTitle = 0.955
xstartPolLabel = 0.05
ystartOverlayLegend = 0.931
opaqueSky = 270. # Kelvin, used for scaling TebbSky

developerEmail = "thunter@nrao.edu"

#class Polarization:
    # taken from Stokes.h in casa, for reference only
#    (Undefined, I,Q,U,V,RR,RL,LR,LL,XX,XY,YX,YY) = range(13)

def version(showfile=True):
    """
    Returns the CVS revision number.
    """
    if (showfile):
        print("Loaded from %s" % (__file__))
    return TASK_PLOTBANDPASS_REVISION_STRING

def refTypeToString(rtype):
    rtypes = ['REST','LSRK','LSRD','BARY','GEO','TOPO','GALACTO','LGROUP','CMB']
    return(rtypes[rtype])

def corrTypeToString(ptype):
    ptypes = ['Undefined','I','Q','U','V','RR','RL','LR','LL','XX','XY','YX','YY']
    mystring = ptypes[ptype]
#    print("mystring = %s" % (mystring))
    return(mystring)

def buildAntString(antID,msFound,msAnt):
    if (msFound):
        antstring = msAnt[antID]
    else:
        antstring = '%02d' % (antID)
    if (antstring.isdigit()):
        Antstring = "Ant %s" % antstring
    else:
        Antstring = antstring
    return(antstring, Antstring)

def makeplot(figfile,msFound,msAnt,overlayAntennas,pages,pagectr,density,
             interactive,antennasToPlot,spwsToPlot,overlayTimes,overlayBasebands,
             locationCalledFrom, xant, ispw, subplot, resample='1',
             debug=False,
             figfileSequential=False, figfileNumber=0):
    if (type(figfile) == str):
        if (figfile.find('/')>=0):
            directories = figfile.split('/')
            directory = ''
            for d in range(len(directories)-1):
                directory += directories[d] + '/'
            if (os.path.exists(directory)==False):
                casalogPost(debug,"Making directory = %s" % (directory))
                os.system("mkdir -p %s" % directory)
    if (debug):
        print(("makeplot(%d): pagectr=%d, len(pages)=%d, len(spwsToPlot)=%d, pages=" % (locationCalledFrom,
                                                              pagectr, len(pages),len(spwsToPlot)), pages))
    if len(pages) == 0:
        t = 0
    elif (pages[pagectr][PAGE_SPW] >= len(spwsToPlot)):
        # necessary for test86: overlay='spw' of spectral scan dataset.  to avoid indexing beyond the
        # end of the array in the the case that the final frame is of a baseband with n spw, and
        # earlier frames had >n spws   2014-04-08
        ispw = spwsToPlot[-1]
    else:
        # CAS-8285: Added 'if' to be sure to use ispw passed in for single-panel plots, but
        # use the original behavior for multi-panel plots simply to preserve the pngfile
        # naming scheme (i.e. including the spw name of lower right panel) to match old
        # regressions.  Should probably remove this whole 'else' block someday, if I don't
        # mind if future multi-panel filenames contain spw name of upper left panel.
        if (subplot != 11 or overlayBasebands):  # Add only this line for CAS-8285.
            ispw = spwsToPlot[pages[pagectr][PAGE_SPW]]
            if debug:
                print("setting ispw to spwsToPlot[pages[pagectr=%d]=%d[PAGE_SPW]] = %d" % (pagectr,pages[pagectr][PAGE_SPW],ispw))
    if len(pages) > 0:
        t = pages[pagectr][PAGE_TIME] # + 1
    if (subplot == 11):
        antstring, Antstring = buildAntString(xant, msFound, msAnt)  # CAS-8285
    else:
        antstring, Antstring = buildAntString(antennasToPlot[pages[pagectr][PAGE_ANT]], msFound, msAnt)
    figfile = figfile.split('.png')[0]
    if (figfileSequential):
        plotfilename = figfile + '.%03d' % (figfileNumber)
    else:
        if (msFound):
            if (overlayAntennas and overlayTimes):
                plotfilename = figfile+'.spw%02d'%(ispw)
            elif (overlayAntennas):
                plotfilename = figfile+'.spw%02d'%(ispw)+'.t%02d'%(t)
            elif (overlayTimes):
                plotfilename = figfile+'.'+antstring+'.spw%02d'%(ispw)
            else:
                plotfilename = figfile+'.'+antstring+'.spw%02d'%(ispw)+'.t%02d'%(t)
        else:
            if (overlayAntennas and overlayTimes):
                plotfilename = figfile+'.spw%02d'%(ispw)
            elif (overlayAntennas):
                plotfilename = figfile+'.spw%02d'%(ispw)+'.t%02d'%(t)
            elif (overlayTimes):
                plotfilename = figfile+'.ant'+antstring+'.spw%02d'%(ispw)
            else:
                plotfilename = figfile+'.ant'+antstring+'.spw%02d'%(ispw)+'.t%02d'%(t)
    if (int(resample) > 1):
        plotfilename += '.resample%d.png' % (int(resample))
    else:
        plotfilename += '.png'
    if (interactive == False or True):
        casalogPost(debug,"Building %s" % (plotfilename))
#        print("Building %s" % (plotfilename))
    pb.savefig(plotfilename, format='png', dpi=density)
    return(plotfilename)

def utdatestring(mjdsec):
    (mjd, dateTimeString) = mjdSecondsToMJDandUT(mjdsec)
    tokens = dateTimeString.split()
    return(tokens[0])

def mjdsecArrayToUTString(timerangeListTimes):
    """
    accepts [4866334935, 4866335281] etc.
    returns '08:04:10, 09:03:00' etc.
    """
    timerangeListTimesString = ''
    for t in timerangeListTimes:
        timerangeListTimesString += utstring(t,3) + ' '
    return(timerangeListTimesString)

def utstring(mjdsec, xframeStart=110):
    (mjd, dateTimeString) = mjdSecondsToMJDandUT(mjdsec)
    tokens = dateTimeString.split()
    hoursMinutes = tokens[1][0:len(tokens[1])-3]
    hoursMinutesSeconds = tokens[1][0:len(tokens[1])]
    if (xframeStart == 110):  # 2011-01-01 UT 00:00
        return(tokens[0]+' '+tokens[2]+' '+hoursMinutes)
    elif (xframeStart == 3):
        return(hoursMinutesSeconds)
    else:  # 00:00
        return(hoursMinutes)

def openBpolyFile(caltable, debug):
   mytb = table()
   mytb.open(caltable)
   desc = mytb.getdesc()
   if ('POLY_MODE' in desc):
      polyMode = mytb.getcol('POLY_MODE')
      casalogPost(debug,"This is a BPOLY solution = %s" % (polyMode[0]))
      polyType = mytb.getcol('POLY_TYPE')
      scaleFactor = mytb.getcol('SCALE_FACTOR')
      antenna1 = mytb.getcol('ANTENNA1')
      times = mytb.getcol('TIME')
      cal_desc_id = mytb.getcol('CAL_DESC_ID')
      nRows = len(polyType)
      for pType in polyType:
          if (pType != 'CHEBYSHEV'):
              casalogPost(debug,"I do not recognized polynomial type = %s" % (pType))
              return
      # Here we assume that all spws have been solved with the same mode
      uniqueTimesBP = np.unique(mytb.getcol('TIME'))
      nUniqueTimesBP = len(uniqueTimesBP)
      mystring = "There are %d unique times in the BPOLY solution: " % (nUniqueTimesBP)
      for u in uniqueTimesBP:
          mystring += '%.3f, ' % (u)
      casalogPost(debug,mystring)
      if (nUniqueTimesBP == 2):
          casalogPost(debug,"differing by %g seconds" % (uniqueTimesBP[1]-uniqueTimesBP[0]))
      nPolyAmp = mytb.getcol('N_POLY_AMP')
      nPolyPhase = mytb.getcol('N_POLY_PHASE')
      frequencyLimits = mytb.getcol('VALID_DOMAIN')
      increments = 0.001*(frequencyLimits[1,:]-frequencyLimits[0,:])
      frequenciesGHz = []
      for i in range(len(increments)):
         freqs = (1e-9)*np.arange(frequencyLimits[0,i],frequencyLimits[1,i],increments[i])
         frequenciesGHz.append(freqs)
      polynomialAmplitude = []
      polynomialPhase = []
      for i in range(len(polyMode)):
          polynomialAmplitude.append([1])
          polynomialPhase.append([0])
          if (polyMode[i] == 'A&P' or polyMode[i] == 'A'):
              polynomialAmplitude[i]  = mytb.getcell('POLY_COEFF_AMP',i)[0][0][0]
          if (polyMode[i] == 'A&P' or polyMode[i] == 'P'):
              polynomialPhase[i] = mytb.getcell('POLY_COEFF_PHASE',i)[0][0][0]

      mytb.close()
      mytb.open(caltable+'/CAL_DESC')
      nSpws = len(mytb.getcol('NUM_SPW'))
      spws = mytb.getcol('SPECTRAL_WINDOW_ID')
      spwBP = []
      for c in cal_desc_id:
          spwBP.append(spws[0][c])
      mytb.done()
      return([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP,
              uniqueTimesBP, frequencyLimits, increments, frequenciesGHz,
              polynomialPhase, polynomialAmplitude, times, antenna1, cal_desc_id, spwBP])
   else:
      mytb.done()
      return([])
   # end of openBpolyFile()

def displayTimesArray(uniqueTimesPerFieldPerSpw):
    """
    Builds a string from an array of MJD second timestamps as UT timestamps
    """
    legendString = ''
    for s in uniqueTimesPerFieldPerSpw:
        legendString += "["
        for f in s:
            legendString += "["
            for t in f:
                legendString += "%s" % utstring(t,2)
                if (t != f[-1]):
                    legendString += ", "
            legendString += "]"
            if (f != s[-1]):
                legendString += ', '
        legendString += "], "
        if (s != uniqueTimesPerFieldPerSpw[-1]):
            legendString += ', '
    return(legendString)

def checkPolsToPlot(polsToPlot, corr_type_string, debug):
    firstFailure = 0
    for pol in polsToPlot:
        if ((pol in corr_type_string) == False):
            casalogPost(debug,"Polarization product %s is not in the ms" % (pol))
            firstFailure += 1
            if (pol in ['XX','YY']):
                polsToPlot = ['RR','LL']
            else:
                polsToPlot = ['XX','YY']
            break
    if (firstFailure>0):
       casalogPost(debug,"Looking for instead: %s" % (str(polsToPlot)))
       for pol in polsToPlot:
          if ((pol in corr_type_string) == False):
              casalogPost(debug,"Polarization product %s is not in the ms" % (pol))
              firstFailure += 1
              if (pol in ['XX']):
                  polsToPlot = ['YY']
              elif (pol in ['YY']):
                  polsToPlot = ['XX']
              elif (pol in ['RR']):
                  polsToPlot = ['LL']
              elif (pol in ['LL']):
                  polsToPlot = ['RR']
              break
    if (firstFailure > 1):
        casalogPost(debug,"Looking for instead: %s" % (str(polsToPlot)))
        for pol in polsToPlot:
            if ((pol in corr_type_string) == False):
                casalogPost(debug,"Polarization product %s is not in the ms" % (pol))
                return([])
    return(polsToPlot)

def getCorrType(msName, spwsToPlot, mymsmd, debug=False):
    """
    Open the DATA_DESCRIPTION_ID table.  Find the polarization_id of the first
    spw in the list of spwsToPlot, then read the CORR_TYPE from the POLARIZATION
    table.
    """
    mytb = table()
    mytb.open(msName+'/DATA_DESCRIPTION')
    spws = mytb.getcol('SPECTRAL_WINDOW_ID')
    polarization_id = mytb.getcol('POLARIZATION_ID')
    mytb.close()
    pol_id = 0
    telescopeName = mymsmd.observatorynames()[0]
    mytb.open(msName+'/POLARIZATION')
    for myspw in spwsToPlot:
#        print("looking for %d in %s" % (myspw, str(spws)))
        row = list(spws).index(myspw)
        if (row >= 0):
            pol_id = polarization_id[row]
            corr_type = mytb.getcell('CORR_TYPE',pol_id)
            if (corr_type[0] >= 5 or (telescopeName.find('ALMA')<0 and telescopeName.find('VLA')<0)):
                # Undefined, I, Q, U, V, which ALMA and VLA never use
                # Need to allow non-VLA, non-ALMA to stop here
                break
    mytb.close()
    corr_type_string = []
    if (len(corr_type) == 4):
        casalogPost(debug,"This is a 4-polarization dataset.")
        if (corr_type[0] in [5,6,7,8]):
            corr_type = [5,8]
        elif (corr_type[0] in [9,10,11,12]):
            corr_type = [9,12]
        else:
            print(("Unsupported polarization types = ", corr_type))
            return(corr_type, corr_type_string)
    # This overrides the len(gain_table) because it can have length=2 even when only 1 pol present
    nPolarizations = len(corr_type)
    for ct in corr_type:
        corr_type_string.append(corrTypeToString(ct))
    if (debug):
        print(("corr_types = ", corr_type,  " = ", corr_type_string))
    return(corr_type, corr_type_string, nPolarizations)

def writeArgument(f,name,arg):
    if (type(arg) == str):
        s = "%-18s = '%s'" % (name,arg)
        t = "%s='%s'" % (name,arg)
    else:
        s = "%-18s = %s" % (name,str(arg))
        t = "%s=%s" % (name,arg)
    f.write(s+'\n')
    return(t)

def channelDifferences(y, x, resample=1):
    """
    Takes a vector, and computes the channel-to-channel derivative.
    Optionally, it will also resample the data and compute the
    derivative.
    - Todd Hunter
    """
    x = np.array(x)
    y = np.array(y)
    if (len(x) > 1):
        channelWidth = x[1]-x[0]
        d = (np.diff(y)/np.diff(x))
        newy = d*channelWidth
        newx = (x[1:]+x[:-1])/2.  # midpoints of input x-axis
    else:
        newx = x
        newy = y
    if (resample > 1):
        x,y = resampleSolution(x,y,resample)
        if (len(x) > 1):
            channelWidth = x[1]-x[0]
            d = (np.diff(y)/np.diff(x))
            resy = d*channelWidth
            resx = (x[1:]+x[:-1])/2.  # midpoints of input x-axis
        else:
            resx = x
            resy = y
    else:
        resy = newy
        resx = newx
    return(newy, newx, resy, resx)

def getDataColumnName(inputMs, debug):
    mytb = table()
    mytb.open(inputMs)
    colnames = mytb.colnames()
    correctedDataColumnName = ''
    modelDataColumnName = ''
    if 'FLOAT_DATA' in colnames:
        dataColumnName = 'FLOAT_DATA'
        correctedDataColumnName = 'FLOAT_DATA'
    elif 'DATA' in colnames:
        dataColumnName = 'DATA'
    if 'CORRECTED_DATA' in colnames:
        correctedDataColumnName = 'CORRECTED_DATA'
    if 'MODEL_DATA' in colnames:
        modelDataColumnName = 'MODEL_DATA'
    mytb.done()
    return(dataColumnName)

def doPolarizations(mymsmd, inputMs, debug=False) :
    # This function is obsolete. There may be no OBSERVE_TARGET intents in a dataset!
    # Determine the number of polarizations for the first OBSERVE_TARGET intent.
    # Used by plotbandpass for BPOLY plots since the number of pols cannot be inferred
    # correctly from the caltable alone.  You cannot not simply use the first row, because
    # it may be a pointing scan which may have different number of polarizations than what
    # the TARGET and BANDPASS calibrator will have.
    # -- T. Hunter
    if (debug): print("doPolarizations()")
    myscan = -1
    starttime = time.time()
    for s in range(1,mymsmd.nscans()+1):
        if (debug): print("s = %s" % (str(s)))
        intents = mymsmd.intentsforscan(s)
        for i in intents:
            if (i.find('OBSERVE_TARGET')>=0):
                myscan = s
#                print("First OBSERVE_TARGET scan = %s" % (str(myscan)))
                break
        if (myscan >= 0):
            break
    if (myscan == -1):
        # if there is no OBSERVE_TARGET, then just use the first scan
        myscan = 0
    dataColumnName = getDataColumnName(inputMs,debug)
    if (debug): print("dataColumnName = %s" % (dataColumnName))
    mytb = table()
    mytb.open("%s" % inputMs)
    if (myscan == 0):
        # assume the first row in the table is for the first scan, to save time
        nPolarizations = np.shape(mytb.getcell(dataColumnName,0))[0]
    else:
        scans = mytb.getcol('SCAN_NUMBER')
        nPolarizations = 0
        for s in range(len(scans)):
            if (scans[s]==myscan):
                nPolarizations = np.shape(mytb.getcell(dataColumnName,s))[0]
                break
    mytb.close()
    donetime = time.time()
    return(nPolarizations)

def getnspw(mymsmd):
#    if (casadef.subversion_revision > '22653'):
        return(mymsmd.nspw(False))
#    else:
#        return(mymsmd.nspw())

def drawOverlayTimeLegends(xframe, firstFrame, xstartTitle, ystartTitle, caltable, titlesize,
                           fieldIndicesToPlot, ispwInCalTable, uniqueTimesPerFieldPerSpw,
                           timerangeListTimes, solutionTimeThresholdSeconds, debugSloppyMatch,
                           ystartOverlayLegend, debug, mysize, fieldsToPlot, myUniqueColor,
                           timeHorizontalSpacing, fieldIndex, overlayColors,
                           antennaVerticalSpacing, overlayAntennas,
                           timerangeList, caltableTitle, mytime, scansToPlot,
                           scansForUniqueTimes, uniqueSpwsInCalTable, uniqueTimes):
    """
    Draws the legend at the top of the page, if it is the correct time to do so,
    including the overlayTimes, the 'UT' label, and the caltable name.
    xframe: an integer as a subplot specifier, like 223 for the third panel of a 2x2 plot
    firstFrame: an integer as a subplot specifier, like 221 for the first panel of a 2x2 plot
    caltable: name of caltable on disk
    titlesize: font size as an integer string, e.g. '7'
    fieldIndicesToPlot: a list of integer indexes to the parent list fieldsToPlot, e.g. [0]  
    fieldsToPlot: list of measurement set field IDs to plot, this can be different (shorter) than parent list fieldsToPlot
    scansToPlot: a list of measurement set scan numbers from which solutions should be plotted
    scansForUniqueTimes: a list of scans corresponding to the parent list of uniqueTimes
    ispwInCalTable: an integer index for the single desired spw in the spw list uniqueSpwsInCalTable, e.g. 0
    uniqueTimesPerFieldPerSpw: a list of list of lists corresponding to [spwIndex][fieldIndex][floating-point times...]
    uniqueTimes: a list of all unique floating-point times in the caltable
    timerangeListTimes: a list of unique values of floating point MJD seconds that were requested to be plotted
    timerangeList: a list of timerange indices that were requested to be plotted
    uniqueSpwsInCalTable: a list of spw IDs, but only used for print statements
    mytime: the index of the timerange in the list of uniqueTimes in the caltable that was 
            being examined when this function was called in the parent 'while loop' over uniqueTimes
    """
    # debugSloppyMatch=True
    myspw = uniqueSpwsInCalTable[ispwInCalTable]
    if debug:
        print("len(timerangeList)=%d, len(timerangeListTimes)=%d" % (len(timerangeList), len(timerangeListTimes)))
        print("timerangeListTimes: %s" % (['%.3f'%(i) for i in timerangeListTimes]))
        print("xframe=%d, firstFrame=%d, ispwInCalTable=%d, spw=%d, fieldIndicesToPlot=%s, fieldsToPlot=%s" % (xframe,firstFrame,ispwInCalTable,myspw,fieldIndicesToPlot,fieldsToPlot))
        print("uniqueTimesPerFieldPerSpw[%d][%d] = %s" % (ispwInCalTable,fieldIndicesToPlot[0],uniqueTimesPerFieldPerSpw[ispwInCalTable][fieldIndicesToPlot[0]]))
    if (xframe == firstFrame):
        # draw title including caltable name
        pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k', transform=pb.gcf().transFigure)
        # support multi-fields with overlay='time'
        uTPFPS = []
        uTPFPStimerange = []

        # Find all timerange indices for all fields, not just the ones that were plotted
        allTimeranges = []
        for f in range(len(uniqueTimesPerFieldPerSpw[ispwInCalTable])):
            for t in uniqueTimesPerFieldPerSpw[ispwInCalTable][f]:
                if (np.min(np.abs(timerangeListTimes-t)) < solutionTimeThresholdSeconds):     ### added 2024Aug  
                    closestIndex = np.argmin(np.abs(timerangeListTimes-t))                    ### added 2024Aug  
                    allTimeranges.append(closestIndex)                                        ### added 2024Aug  
                        
        allTimeranges = list(np.sort(np.unique(allTimeranges)))
        # allTimeranges is a list of integers.
        # The length of allTimeranges will generally be shorter than timerangeListTimes.
        
        for f in fieldIndicesToPlot:
            found = 0
            for timestamp in uniqueTimesPerFieldPerSpw[ispwInCalTable][f]:
                if timestamp in uniqueTimes:                                 # 2024Aug28
                    myUniqueTimeIndex = uniqueTimes.index(timestamp)         # 2024Aug28
                    matched, mymatch = sloppyMatch(timestamp, timerangeListTimes, solutionTimeThresholdSeconds, myUniqueTimeIndex, scansToPlot,  # 2024Aug28
                                                   scansForUniqueTimes, myprint=debugSloppyMatch, whichone=True)
                    # when there are multiple solutions per scan (as in SDSKY_PS tables), the value of mymatch will always be the first one, so cannot use it
                    mymatch = myUniqueTimeIndex  # 2024Aug28
                else:
                    # Not including the mytime, scansToPlot and scansForUniqueTimes in the following call will lead to the legend
                    # showing all timestamps, not merely the subset that was selected by the (optional) scans parameter.  That is
                    # why we need to compare each myUniqueTimeIndex.  You cannot simply use mytime as it will always be the final
                    # time in the parent loop when you are in the overlay='time' scenario.  But we fail over to this method.
                    if debug:
                        print("%.3f not in uniqueTimes, failing over to simpler method" % (timestamp))
                    matched, mymatch = sloppyMatch(timestamp, timerangeListTimes, solutionTimeThresholdSeconds, myprint=debugSloppyMatch, whichone=True) # 2024Aug27 regression
                if (matched and mymatch in timerangeList):
                    uTPFPS.append(timestamp)
                    uTPFPStimerange.append(mymatch)
            expected = len(uniqueTimesPerFieldPerSpw[ispwInCalTable][f])
            if found < expected:
                statement = "plotbandpass drawOverlayTimeLegends() found %d/%d time matches for spw%d field%d" % (found,expected,myspw,f)
                casalogPost(debug,statement)
        # sort the timeranges so the text labels will be increasing   
        idx = np.argsort(uTPFPS)
        uTPFPStimerange = np.array(uTPFPStimerange)[idx]
        uTPFPS = np.sort(uTPFPS)
        timeFormat = 3  # HH:MM:SS
        maxTimesAcross = maxTimesAcrossTheTop
        if (firstFrame == 111):
            maxTimesAcross -= 2

        for a in range(len(uTPFPS)):
            legendString = utstring(uTPFPS[a],timeFormat)
            if (debug): print("----> Defined legendString: %s" % (legendString))
            if (a==0):
                pb.text(xstartTitle-0.03, ystartOverlayLegend, 'UT',color='k',fontsize=mysize,
                        transform=pb.gcf().transFigure)
            if (a < maxTimesAcross):
                x0 = xstartTitle + (a*timeHorizontalSpacing)
                y0 = ystartOverlayLegend
            else:
                # start going down the righthand side
                x0 = xstartTitle + (maxTimesAcross*timeHorizontalSpacing)
                y0 = ystartOverlayLegend-(a-maxTimesAcross)*antennaVerticalSpacing
            if (True):
                if (debug):
                    print("3)checking time %d, len(uTPFPS)=%d" % (a,len(uTPFPS)))
                if (sloppyMatch(uTPFPS[a],timerangeListTimes,
                                solutionTimeThresholdSeconds,
                                mytime, scansToPlot, scansForUniqueTimes,
                                myprint=debugSloppyMatch)):
                    myUniqueTime = uTPFPS[a]
                    if (debug):
                        print("3)setting myUniqueTime to %d" % (myUniqueTime))

            if (debug): print("----> Drawing legendString: %s" % (legendString))
            if ((len(fieldsToPlot) > 1 or len(timerangeList) > 1) and overlayAntennas==False):
                # having overlayAntennas==False here will force all time labels to be black (as desired)
                if (debug):
                    print("len(uTPFPS)=%d, a=%d, len(myUniqueColor)=%d, overlayColors[%d]=%s" % (len(uTPFPS),a,len(myUniqueColor),timerangeList[allTimeranges.index(uTPFPStimerange[a])],str(overlayColors[timerangeList[allTimeranges.index(uTPFPStimerange[a])]])))
                    print("len(uTPFPS)=%d, a=%d, len(myUniqueColor)=%d" % (len(uTPFPS),a,len(myUniqueColor)))

                mycolor = overlayColors[uTPFPStimerange[a]]  # color based on the subset of timeranges to be plotted
                pb.text(x0, y0, legendString, color=mycolor, fontsize=mysize, transform=pb.gcf().transFigure)
            else:  # only 1 spectrum, or overlayAntennas==True means use all black labels
                pb.text(x0, y0, legendString, fontsize=mysize, transform=pb.gcf().transFigure)

def lineNumber():
    """Returns the current line number in our program."""
    return inspect.currentframe().f_back.f_lineno

def drawAtmosphereAndFDM(showatm, showtsky, atmString, subplotRows, mysize, TebbSky,
                         TebbSkyImage,plotrange, xaxis, atmchan, atmfreq, transmission,
                         subplotCols, showatmPoints,xframe, channels,LO1,atmchanImage,
                         atmfreqImage,transmissionImage, firstFrame,showfdm,nChannels,tableFormat,
                         originalSpw_casa33, chanFreqGHz_casa33,originalSpw,chanFreqGHz,
                         overlayTimes, overlayAntennas, xant, antennasToPlot, overlaySpws,
                         baseband, showBasebandNumber, basebandDict, overlayBasebands, overlayColors,
                         drewAtmosphere, showtsys=False, Trx=None):
    """
    If requested by the user at the command line, draw the atmospheric curve
    and the FDM window locations.
    """
    mylineno = lineNumber()
    ylim = pb.ylim()  # CAS-8655
    if ((showatm or showtsky) and len(atmString) > 0):
        ylim = DrawAtmosphere(showatm, showtsky, subplotRows, atmString,
                       mysize, TebbSky, plotrange, xaxis, atmchan,
                       atmfreq, transmission, subplotCols,
                       showatmPoints=showatmPoints, xframe=xframe,
                       channels=channels,
                       mylineno=mylineno,xant=xant,
                       overlaySpws=overlaySpws, overlayBasebands=overlayBasebands,
                       drewAtmosphere=drewAtmosphere,
                       loc=201, showtsys=showtsys, Trx=Trx)
        if (LO1):
            # Now draw the image band
            ylim = DrawAtmosphere(showatm,showtsky, subplotRows, atmString,
                           mysize, TebbSkyImage, plotrange, xaxis,
                           atmchanImage, atmfreqImage, transmissionImage,
                           subplotCols, LO1, xframe, firstFrame, showatmPoints,
                           channels=channels, mylineno=mylineno,xant=xant,
                           overlaySpws=overlaySpws, overlayBasebands=overlayBasebands,
                           drewAtmosphere=drewAtmosphere,
                           loc=202, showtsys=showtsys, Trx=Trx)
    # The following case is needed for the case that overlay='antenna,time' and
    # the final timerange is flagged on the final antenna.
#    if (overlayTimes==False or overlayAntennas==False or xant==antennasToPlot[-1]):
    # Because this function is now only called from one place, setting this to
    # True is what we want. - 18-Jun-2013
    if (True):
        if (xaxis.find('freq')>=0 and showfdm and nChannels <= 256):
            if (tableFormat == 33):
                showFDM(originalSpw_casa33, chanFreqGHz_casa33,
                        baseband, showBasebandNumber, basebandDict, overlayColors)
            else:
                showFDM(originalSpw, chanFreqGHz,
                        baseband, showBasebandNumber, basebandDict, overlayColors)
            ylim = pb.ylim()  # CAS-11062 need to pass the new wider limits back up to calling function
    return ylim  # CAS-8655

def DrawPolarizationLabelsForOverlayTime(xstartPolLabel,ystartPolLabel,corr_type,polsToPlot,
                                         channeldiff,ystartMadLabel,subplotRows,gamp_mad,mysize,
                                         ampmarkstyle,markersize,ampmarkstyle2, gamp_std):
    """
    Currently this is only called for amp vs. X plots. The corresponding code for phase
    vs. X plots is still inside plotbandpass().  But this is okay because overlay='time'
    is mainly intended for Tsys plots.
    """
    x0 = xstartPolLabel
    y0 = ystartPolLabel
    if (corrTypeToString(corr_type[0]) in polsToPlot):
        if (channeldiff > 0):
            pb.text(x0, ystartMadLabel-0.03*subplotRows*0,
                    corrTypeToString(corr_type[0])+' MAD = %.4f, St.Dev = %.4f'%(gamp_mad[0]['mad'], gamp_std[0]['std']),
                    color='k',size=mysize, transform=pb.gca().transAxes)
        if (ampmarkstyle.find('-')>=0):
            pb.text(x0, y0, corrTypeToString(corr_type[0])+' solid', color='k',
                    size=mysize, transform=pb.gca().transAxes)
        else:
            pb.text(x0+0.02, y0, corrTypeToString(corr_type[0]), color='k',
                    size=mysize, transform=pb.gca().transAxes)
            pdesc = pb.plot([x0-0.1], [y0], '%sk'%ampmarkstyle, markersize=markersize,
                            scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
    if (len(corr_type) > 1):
        if (corrTypeToString(corr_type[1]) in polsToPlot):
            if (channeldiff > 0):
                pb.text(x0, ystartMadLabel-0.03*subplotRows*1,
                        corrTypeToString(corr_type[1])+' MAD = %.4f, St.Dev = %.4f'%(gamp_mad[1]['mad'], gamp_std[1]['std']),
                        color='k',size=mysize, transform=pb.gca().transAxes)
            if (ampmarkstyle2.find('--')>=0):
                pb.text(x0, y0-0.03*subplotRows, corrTypeToString(corr_type[1])+' dashed',
                        color='k', size=mysize, transform=pb.gca().transAxes)
            else:
                pb.text(x0, y0-0.03*subplotRows, corrTypeToString(corr_type[1]), # removed +0.02*xrange on 11-Mar-2014
                        color='k', size=mysize, transform=pb.gca().transAxes)
                pdesc = pb.plot([x0-0.1], [y0-0.03*subplotRows], '%sk'%ampmarkstyle2,
                                markersize=markersize, scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)

def plural(u):
    """
    If the length of the array passed is > 1, return 's', otherwise return ''.
    """
    if (len(u) > 1):
        return('s')
    else:
        return('')

def casalogPost(debug, mystring):
    casalog.post(mystring)
    if (debug): print(mystring)

def computeHighestSpwIndexInSpwsToPlotThatHasCurrentScan(spwsToPlot, scansToPlotPerSpw, scan):
    highestSpwIndex = -1
    for i,spw in enumerate(spwsToPlot):
        if (scan in scansToPlotPerSpw[spw]):
            highestSpwIndex = i
    return(highestSpwIndex)

def madOfDiff(solution):
    """
    This function is used to decide which of two curves has more scatter, and hence should
    be plotted first (i.e. shown in the background) when overlaying two solutions.
    Added as part of CAS-9474 to do a better job of the selection
    """
    if (len(solution) < 4):
        return MAD(np.diff(solution))
    else:
        start = int(len(solution)/4)
        stop = int(len(solution)*3/4)
        ydata = np.array(solution[start:stop+1])
        return MAD(np.diff(ydata))
        
def run_with_old_pyplot_style(func):
    """
    CAS-12786: this decorator is introduced here to have a single entry point before/after
    which the style sheet of matplotlib/pylab can be tuned and restored before returning.

    The motivation is that plotbandpass plots are heavily dependent on default image size,
    grid style, ticks frequency, etc., as it used to be in matplotlib 1.1.0 (used in
    CASA 5.x). In more recent matplotlib (3.1.1 is currently used in CASA 6.x) that style
    can be reproduced using the style sheet 'classic'. This is the quickest and simplest way
    to produce plotbandpass plots that look like those of CASA 5
    # https://matplotlib.org/3.1.1/users/dflt_style_changes.html
    """
    def func_old_style(*args, **kwargs):
        with pb.style.context('classic'):
            pb.rc('axes.formatter', useoffset=False)
            pb.rc('axes', grid=True)
            pb.rc('axes.grid', axis='both', which='major')
            return func(*args, **kwargs)

    return func_old_style

DEFAULT_PLATFORMING_THRESHOLD = 10.0 # unused if platformingSigma != 0
@run_with_old_pyplot_style
def plotbandpass(caltable='', antenna='', field='', spw='', yaxis='amp',
                 xaxis='chan', figfile='', plotrange=[0,0,0,0],
                 caltable2='', overlay='', showflagged=False, timeranges='',
                 buildpdf=False, caltable3='', markersize=3, density=108,
                 interactive=True, showpoints='auto', showlines='auto',
                 subplot='22', zoom='', poln='', showatm=False, pwv='auto',
                 gs='gs', convert='convert', chanrange='',
                 solutionTimeThresholdSeconds=30.0, debug=False,
                 phase='', vis='',showtsky=False, showfdm=False,showatmfield='',
                 lo1='', showimage=False, showatmPoints=False, parentms='',
                 pdftk='pdftk', channeldiff=False, edge=8, resample=1,
                 platformingThreshold=DEFAULT_PLATFORMING_THRESHOLD,
                 platformingSigma=5.0, basebands=[], showBasebandNumber=False,
                 scans='', figfileSequential=False, chanrangeSetXrange=False,
                 Trx='auto', showtsys=False):
    """
    This is a task to plot bandpass and Tsys calibration tables faster and more
    flexibly than plotcal, including the ability to overlay the atmospheric
    transmission, and to create multi-page plots as pngs and combine them
    into single PDF documents.
    It works with both the old style and new style cal tables.  The source code
    is in task_plotbandpass.py.  For more detailed help, see examples at:
    http://casaguides.nrao.edu/index.php?title=Plotbandpass
    -- Todd Hunter
    """
    def safe_pb_subplot(xframe):
        """
        CAS-12786: old pyplots (up to CASA 5.6.1 used to accept "220" in the pos parameter
        Newer pyplots won't. Assume the index effectively used was 1 ("221")
        """
        xf = (xframe + 1) if str(xframe).endswith('0') else xframe
        return pb.subplot(xf)

    def safe_pb_clf():
        pb.clf()

    casalog.origin('plotbandpass')
    casalogPost(debug,"task plotbandpass version number:\t%s" % (TASK_PLOTBANDPASS_REVISION_STRING))
    casalogPost(debug,"tracking au version:\t\t\t%s" % (PLOTBANDPASS_REVISION_STRING))
    DEBUG = debug
    help = False
    vm = '' # unused variable, now that msmd is available in casa
    if (help):
        print("Usage: plotbandpass(caltable='', antenna='', field='', spw='', yaxis='amp',")
        print("   xaxis='chan', figfile='', plotrange=[0,0,0,0], caltable2='',")
        print("   overlay='', showflagged=False, timeranges='', buildpdf=False, caltable3='',")
        print("   markersize=3, density=108, interactive=True, showpoints='auto',")
        print("   showlines='auto', subplot='22', zoom='', poln='', showatm=False, pwv='auto',")
        print("   gs='gs', convert='convert', chanrange='', debug=False,")
        print("   solutionTimeThresholdSeconds=30.0, phase='', vis='', showtsky=False,")
        print("   showfdm=False, showatmfield='', lo1='', showimage=False,")
        print("   showatmPoints=False, parentms='', pdftk='pdftk', channeldiff=False,")
        print("   edge=8, resample=1, vis='',platformingThreshold=%f," % (DEFAULT_PLATFORMING_THRESHOLD))
        print("   platformingSigma=%.1f, basebands=[], showBasebandNumber=False," % (5.0))
        print("   scans='')")
        print(" antenna: must be either an ID (int or string or list), or a single antenna name or list")
        print(" basebands: show only spws from the specified baseband or list of basebands (default:None=all)")
        print(" buildpdf: True/False, if True and figfile is set, assemble pngs into a pdf")
        print(" caltable: a bandpass table, of type B or BPOLY")
        print(" caltable2: a second cal table, of type BPOLY or B, to overlay on a B table")
        print(" caltable3: a third cal table, of type BPOLY, to overlay on the first two")
        print(" chanrange: set xrange ('5~100') over which to autoscale y-axis for xaxis='freq'")
        print(" channeldiff: set to value > 0 (sigma) to plot derivatives of amplitude")
        print(" convert: full path for convert command (in case it's not found)")
        print(" density: dpi to use in creating PNGs and PDFs (default=108)")
        print(" edge: the number of edge channels to ignore in finding outliers (for channeldiff>0)")
        print(" field: must be an ID, source name, or list thereof; can use trailing *: 'J*'")
        print(" figfile: the base_name of the png files to save: base_name.antX.spwY.png")
        print(" figfileSequential: naming scheme, False: name by spw/antenna (default)")
        print("                    True: figfile.1.png, figfile.2.png, etc.")
        print(" gs: full path for ghostscript command (in case it's not found)")
        print(" interactive: if False, then figfile will run to completion automatically and no gui")
        print(" lo1: specify the LO1 setting (in GHz) for the observation")
        print(" overlay: 'antenna','time','antenna,time','spw', or 'baseband'")
        print("        makes 1 plot with different items in colors")
        print(" markersize: size of points (default=3)")
        print(" vis: name of the ms for this table, in case it does not match the string in the caltable")
        print(" parentms: name of the parent ms, in case the ms has been previously split")
        print(" pdftk: full path for pdftk command (in case it's not found)")
        print(" phase: the y-axis limits to use for phase plots when yaxis='both'")
        print(" platformingSigma: declare platforming if the amplitude derivative exceeds this many times the MAD")
        print(" platformingThreshold: if platformingSigma=0, then declare platforming if the amplitude")
        print("                       derivative exceeds this percentage of the median")
        print(" plotrange: define axis limits: [x0,x1,y0,y1] where 0,0 means auto")
        print(" poln: polarizations to plot (e.g. 'XX','YY','RR','LL' or '' for both)")
        print(" pwv: define the pwv to use for the showatm option: 'auto' or value in mm")
        print(" resample: channel expansion factor to use when computing MAD of derivative (for channeldiff>0)")
        print(" scans: show only solutions for the specified scans (int, list, or string)")
        print(" showatm: compute and overlay the atmospheric transmission curve")
        print(" showatmfield: for overlay='time', use first observation of this fieldID or name")
        print(" showatmPoints: draw atmospheric curve with points instead of a line")
        print(" showBasebandNumber: put the BBC_NO in the title of each plot")
        print(" showfdm: when showing TDM spws with xaxis='freq', draw locations of FDM spws")
        print(" showflagged:  show the values of data, even if flagged")
        print(" showimage: also show the atmospheric curve for the image sideband (in black)")
        print(" showtsky: compute and overlay the sky temperature curve instead of transmission")
        print(" showlines: draw lines connecting the data (default=T for amp, F for phase)")
        print(" showpoints: draw points for the data (default=F for amp, T for phase)")
        print(" solutionTimeThresholdSeconds: consider 2 solutions simultaneous if within this interval (default=30)")
        print(" spw: must be single ID or list or range (e.g. 0~4, not the original ID)")
        print(" subplot: 11..81,22,32 or 42 for RowsxColumns (default=22), any 3rd digit is ignored")
        print(" timeranges: show only these timeranges, the first timerange being 0")
        print(" xaxis: 'chan' or 'freq'")
        print(" yaxis: 'amp', 'tsys', 'phase', or 'both' amp&phase == 'ap'; append 'db' for dB")
        print(" zoom: 'intersect' will zoom to overlap region of caltable with caltable2")
        return()
    mytimestamp = time.time()
    debugSloppyMatch = debug
    doneOverlayTime = False  # changed from True on 08-nov-2012
    missingCalWVRErrorPrinted = False
    adesc = None
    # initialize the arguments to DrawAtmosphereAndFDM()
    TebbSky = None
    TebbSkyImage = None
    atmchan = None
    atmfreq = None
    transmission = None
    atmchanImage = None
    atmfreqImage = None
    transmissionImage = None
    originalSpw_casa33 = None
    originalSpw = None
    chanFreqGHz_casa33 = None
    chanFreqGHz = None
    # initialize arguments to DrawPolarizationLabelsForOverlayTime()
    gamp_mad = None
    gamp_std = None
    figfileNumber = 0  # only used if figfileSequential == True

    if (False):
      # Write a .last file
      if (os.access('plotbandpass.last',os.W_OK)):
        cmd = 'plotbandpass'
        lastfile = open('%s.last'%cmd,'w')
        lastfile.write('taskname           = "%s"\n'%cmd)
        cmd += '(' + writeArgument(lastfile, "caltable", caltable)
        cmd += ',' + writeArgument(lastfile, "antenna" , antenna)
        cmd += ',' + writeArgument(lastfile, "field" , field)
        cmd += ',' + writeArgument(lastfile, "spw" , spw)
        cmd += ',' + writeArgument(lastfile, "yaxis", yaxis)
        cmd += ',' + writeArgument(lastfile, "xaxis", xaxis)
        cmd += ',' + writeArgument(lastfile, "figfile", figfile)
        cmd += ',' + writeArgument(lastfile, "plotrange" , plotrange)
        cmd += ',' + writeArgument(lastfile, "caltable2", caltable2)
        cmd += ',' + writeArgument(lastfile, "overlay", overlay)
        cmd += ',' + writeArgument(lastfile, "showflagged", showflagged)
        cmd += ',' + writeArgument(lastfile, "timeranges", timeranges)
        cmd += ',' + writeArgument(lastfile, "buildpdf", buildpdf)
        cmd += ',' + writeArgument(lastfile, "caltable3", caltable3)
        cmd += ',' + writeArgument(lastfile, "markersize", markersize)
        cmd += ',' + writeArgument(lastfile, "density", density)
        cmd += ',' + writeArgument(lastfile, "interactive", interactive)
        cmd += ',' + writeArgument(lastfile, "showpoints", showpoints)
        cmd += ',' + writeArgument(lastfile, "showlines", showlines)
        cmd += ',' + writeArgument(lastfile, "subplot", subplot)
        cmd += ',' + writeArgument(lastfile, "zoom", zoom)
        cmd += ',' + writeArgument(lastfile, "poln", poln)
        cmd += ',' + writeArgument(lastfile, "showatm", showatm)
        cmd += ',' + writeArgument(lastfile, "showatmfield", showatmfield)
        cmd += ',' + writeArgument(lastfile, "pwv", pwv)
        cmd += ',' + writeArgument(lastfile, "gs", gs)
        cmd += ',' + writeArgument(lastfile, "convert", convert)
        cmd += ',' + writeArgument(lastfile, "chanrange", chanrange)
        cmd += ',' + writeArgument(lastfile, "solutionTimeThresholdSeconds", solutionTimeThresholdSeconds)
        cmd += ',' + writeArgument(lastfile, "debug", debug)
        #  cmd += ',' + writeArgument(lastfile, "vm", vm)
        cmd += ',' + writeArgument(lastfile, "phase", phase)
        cmd += ',' + writeArgument(lastfile, "vis", vis)
        cmd += ',' + writeArgument(lastfile, "parentms", parentms)
        cmd += ',' + writeArgument(lastfile, "lo1", lo1)
        cmd += ',' + writeArgument(lastfile, "showimage", showimage)
        cmd += ',' + writeArgument(lastfile, "showtsky", showtsky)
        cmd += ',' + writeArgument(lastfile, "showatmPoints", showatmPoints)
        cmd += ',' + writeArgument(lastfile, "showfdm", showfdm)
        cmd += ',' + writeArgument(lastfile, "pdftk", pdftk) + ')'
        lastfile.write('#%s\n'%(cmd))
        lastfile.close()

    LO1 = None  # Fix for SCOPS-4877
    lo1s = None # Fix for SCOPS-4877
    if (showimage == False):
        LO1 = lo1 = ''
    elif (lo1 != ''):
        if re.match("^\d+?\.\d+?$", lo1) is None:
                print("lo1 must be a float (entered as a string or number)")
                return
        lo1 = float(lo1)
        if (lo1 > 1e6):
            # convert from Hz to GHz
            lo1 *= 1e-9
    if (showatm and showtsky):
        print("You have selected both showatm and showtsky!  Defaulting to showatm=True only.")
        showtsky = False
    if (showatm==False and showtsky==False and showatmfield!=''):
        print("Defaulting to showatm=True because showatmfield was specified.")
        showatm = True
    if (showatm==False and showtsky==False and showimage==True):
        print("Defaulting to showatm=True because showimage was True.")
        showatm = True
    if showtsys:
        showtsky = True
    if (overlay.find('time') < 0 and showatmfield != ''):
        print("The showatmfield only has meaning for overlay='time'.")
        return()

    if (plotrange=='' or plotrange==[]):
        plotrange = [0,0,0,0]
    if (type(plotrange) != list):
        print("plotrange must be an array: e.g. [0,1,-180,180]")
        return()
    if (len(plotrange) < 4):
        print("plotrange must be an array: e.g. [0,1,-180,180]")
        return()
    if (phase != ''):
        if (type(phase) != list):
            print("phase must be either '' or 2 values: [x,y]")
            return()
        if (len(phase) != 2):
            print("phase must be either '' or 2 values: [x,y]")
            return()

    if (edge < 0):
        print("edge must be >= 0")
        return(vm)

    if (resample < 1):
        print("resample must be an integer >= 1")
        return(vm)
    resample = int(resample)

    if (buildpdf and figfile==''):
        print("With buildPDF=True, you must specify figfile='yourFileName' (.png will be appended if necessary).")
        return()

    if (interactive==False and figfile=='' and channeldiff == False):
        print("With interactive=False and channeldiff=False, you must specify figfile='yourFileName' (.png will be appended if necessary).")
        return()

    pxl = 0 # polarization number to use for setting xlimits if plotrange=[0,0...]
    chanrangePercent = None
    if (type(chanrange) != str):
        if (type(chanrange) != list):
            print("Chanrange must be a string or list:  '8~120' or [8,120]")
            return()
        elif (len(chanrange) != 2):
            print("Chanrange must be a string or list:  '8~120' or [8,120]")
            return()
        elif ((type(chanrange[0]) != int) or (type(chanrange[1]) != int)):
            print("Chanrange list members must be integers, not %s, %s" % (type(chanrange[0]), type(chanrange[1])))
            return
    elif (len(chanrange) < 1):
        chanrange = [0,0]
    else:
        if (chanrange.find('%')>0):
            chanrangePercent = float(chanrange.split('%')[0])
            if (chanrangePercent >= 100 or chanrangePercent <= 0):
                chanrangePercent = None
            chanrange = [0,0]
        elif (chanrange.find('~')>=0):
            tokens = chanrange.split('~')
            if (len(tokens) < 2):
                print("Invalid chanrange string, too few tokens")
                return()
            try:
                chanrange = [int(tokens[0]),int(tokens[1])]
                if (DEBUG):
                    print("Using chanrange = %s" % (str(chanrange)))
            except:
                print("Invalid chanrange, not integers")
                return()
        else:
            print("Invalid chanrange, no tilde or percent sign found")
            return()
        if (xaxis.find('chan')>=0):
            print("The chanrange parameter is only valid for xaxis='freq', and only if the plotrange is [0,0,0,0].")
            return()
    if (chanrange[0] < 0):
        print("Invalid chanrange, cannot be negative")
        return()
    if ((chanrange[0] != 0 or chanrange[1] != 0 or chanrangePercent != None) and
        (plotrange[0] != 0 or plotrange[1] != 0 or plotrange[2] != 0 or plotrange[3] != 0)):
        print("If chanrange is specified, then plotrange must be all zeros.")
        return()

    if (pwv==''):
        pwv = 1.0

    # CAS-12786: from a command that sets  poln='' we'll get poln as ['']
    if isinstance(poln, list) and 1 == len(poln):
        poln = poln[0]

    if (type(poln) != list):
          poln = poln.upper()
    if (poln == 'X'):
          poln = 'XX'
    if (poln == 'Y'):
          poln = 'YY'
    if (poln == 'X,Y' or poln=='Y,X'):
          poln = 'XX,YY'
    if (poln == 'R'):
          poln = 'RR'
    if (poln == 'L'):
          poln = 'LL'
    if (poln == 'R,L' or poln=='L,R'):
          poln = 'RR,LL'

    # Parse the polarizations to plot from the command line
    # Prior to opening the .ms (later), we cannot tell which products are actually present
    useAllPols = False
    if (poln == ''):
        useAllPols = True
        polsToPlot = ['XX','YY']  # assume ALMA initially
    elif (type(poln) == list):
        polsToPlot = poln
    else:
        if ((poln in ['','RR','RL','LR','LL','XX','XY','YX','YY','RR,LL','XX,YY']) == False):
            print("Unrecognized polarization option = %s" % (poln))
            return()
        if (poln.find(',')>0):
            polsToPlot = poln.split(',')
        else:
            polsToPlot = [poln]


    if ((overlay in ['antenna', 'spw', 'time', 'baseband', '',
                     'antenna,time', 'time,antenna']) == False):
        print("Unrecognized option for overlay: only 'antenna', 'spw', 'baseband', 'time' and 'antenna,time' are supported.")
        return()

    allowedFrames = [11,21,31,41,51,61,71,81,22,32,42] # [11,22,32,42]
    if (int(subplot) > 100):
        # This will accept 111, 221, 321, 421, etc.
        subplot //= 10
    if ((int(subplot) in allowedFrames)==False):
      print("Subplot choice (rows x columns) must be one of %s" % (str(allowedFrames)))
      print("(with an optional trailing digit that is ignored).")
      return()

    if ((int(subplot) % 2) == 1):
        timeHorizontalSpacing = 0.06*1.3 # *1.3 is for HH:MM:SS (timeFormat=3 in drawOverlayTimeLegends)
    else:
        timeHorizontalSpacing = 0.05*1.3 # *1.3 is for HH:MM:SS

    if (yaxis.find('both')<0 and yaxis.find('ap')<0 and yaxis.find('tsys')<0 and
        yaxis.find('amp')<0 and yaxis.find('phase')<0):
        print("Invalid yaxis.  Must be 'amp', 'tsys', 'phase' or 'both'.")
        return()

    if (yaxis.find('tsys')>=0):
        yaxis = 'amp'

    if (xaxis.find('chan')<0 and xaxis.find('freq')<0):
        print("Invalid xaxis.  Must be 'chan' or 'freq'.")
        return()

    if (showatm and showtsky):
        print("showatm=True and showtsky=True are mutually exclusive options")
        return()

    if (showfdm and xaxis.find('freq')<0):
        print("The option showfdm=True requires xaxis='freq'.")
        return()

    # Plotting settings
    minPhaseRange = 0.2
    plotfiles = []
    if (int(subplot) % 2 == 1):
      mysize = '10'
      titlesize = 10
    elif (int(subplot) == 22 or int(subplot) == 32):
      mysize = '8'
      titlesize = 8
    else:
      mysize = '7'
      titlesize = 8
    maxCharsBeforeReducingTitleFontSize = 72
    if (type(subplot) == str):
        subplot = int(subplot)
    if (subplot in allowedFrames == False):
        print("Invalid subplot = %d.  Valid options are: %s" % (subplot,str(allowedFrames)))
        return()
    xframeStart = int(subplot)*10  # i.e. 110 or 220 or 420
    firstFrame = xframeStart + 1
    lastFrame = xframeStart + (subplot//10)*(subplot%10)
    bottomRowFrames = [111,212,313,414,515,616,717,818,223,224,325,326,427,428]  # try to make this more general
    leftColumnFrames = [111,211,212,311,312,313,411,412,413,414,511,512,513,514,515,611,612,613,614,615,616,
                      711,712,713,714,715,716,717,811,812,813,814,815,816,817,818,221,223,321,323,325,421,423,425,427]
    rightColumnFrames = [111,211,212,311,312,313,411,412,413,414,511,512,513,514,515,611,612,613,614,615,616,
                       711,712,713,714,715,716,717,811,812,813,814,815,816,817,818,222,224,322,324,326,422,424,426,428]
    subplotCols = subplot % 10
    subplotRows = subplot//10
    ystartPolLabel = 1.0-0.04*subplotRows
    ystartMadLabel = 0.04*subplotRows
    if (subplotCols == 1):
        fstringLimit = 40 # character length of multi-field overlay title string
    elif (subplotCols == 2):
        fstringLimit = 12 # character length of multi-field overlay title string

    xframe = xframeStart
    previousSubplot = xframe
    xcolor = 'b'
    ycolor = 'g'
    pcolor = ['b','g']
    x2color = 'k'
    y2color = 'c'
    p2color = ['k','c']
    x3color = 'm'
    y3color = 'r'
    p3color = ['m','r']
    if (showpoints == 'auto'):
        if (showlines == 'auto'):
            ampmarkstyle = '-'
            phasemarkstyle = '.'
            if (len(polsToPlot) == 1):
                  ampmarkstyle2 = '-'
            else:
                  ampmarkstyle2 = '--'
            phasemarkstyle2 = 'o'
        elif (showlines == False):
            ampmarkstyle = '.'
            ampmarkstyle2 = 'o'
            phasemarkstyle = '.'
            phasemarkstyle2 = 'o'
        else:
            ampmarkstyle = '-'
            phasemarkstyle = '-'
            if (len(polsToPlot) == 1):
                  ampmarkstyle2 = '-'
                  phasemarkstyle2 = '-'
            else:
                  ampmarkstyle2 = '--'
                  phasemarkstyle2 = '--'
    elif (showpoints == True):
        if (showlines == 'auto'):
            ampmarkstyle = '.-'
            phasemarkstyle = '.'
            if (len(polsToPlot) == 1):
                  ampmarkstyle2 = 'o-'
            else:
                  ampmarkstyle2 = 'o--'
            phasemarkstyle2 = 'o'
        elif (showlines == False):
            ampmarkstyle = '.'
            ampmarkstyle2 = 'o'
            phasemarkstyle = '.'
            phasemarkstyle2 = 'o'
        else:
            ampmarkstyle = '.-'
            phasemarkstyle = '.-'
            if (len(polsToPlot) == 1):
                  ampmarkstyle2 = 'o-'
                  phasemarkstyle2 = 'o-'
            else:
                  ampmarkstyle2 = 'o--'
                  phasemarkstyle2 = 'o--'
    else:  # showpoints == False
        if (showlines == False):
            print('You must have either showpoints or showlines set True or auto, assuming showlines=T')
        ampmarkstyle = '-'
        phasemarkstyle = '-'
        if (len(polsToPlot) == 1):
              ampmarkstyle2 = '-'
              phasemarkstyle2 = '-'
        else:
              ampmarkstyle2 = '--'
              phasemarkstyle2 = '--'

    ampmarkstyles = [ampmarkstyle,ampmarkstyle2]
    phasemarkstyles = [phasemarkstyle,phasemarkstyle2]
    # bpoly solutions should always be shown as lines, not dots or dots+lines
    bpolymarkstyle = '-'

    amplitudeWithPhase = (yaxis.find('both')>=0 or yaxis.find('ap')>=0)
    if (amplitudeWithPhase):
        myhspace = 0.30
        if (overlay.find('antenna')>=0 or overlay.find('time')>=0  or overlay.find('spw')>=0):
            print("Option 'overlay' is incompatible with yaxis='both'.  Pick either amp or phase.")
            return()
    else:
        myhspace = 0.30
    if (subplot//10 > 2):
        myhspace = 0.4
    if (subplot//10 > 3):
        myhspace = 0.6
    mywspace = 0.25

    # Now open the Bandpass solution table
    if (len(caltable) < 1):
        print("You need to specify a caltable.")
        return(vm)
    if (caltable[-1] == '/'):
        print("Stripping off the trailing '/' from the caltable name.")
        caltable = caltable[:-1]
    mytb = table()
    try:
        if (DEBUG): print("Trying to open: %s." % (caltable))
        mytb.open(caltable)
    except:
        print("Could not open the caltable = %s" % (caltable))
        return()
    if (caltable[0] != '/'):
        # print(this so when someone sends me a bug report I can find their data!)
        try:
            print("caltable = %s:%s/%s" % (os.uname()[1], os.getcwd(), caltable))
        except:
            print("caltable = localhost:%s/%s" % (os.getcwd(), caltable))
    else:
        try:
            print("caltable = %s:%s" % (os.uname()[1], caltable))
        except:
            print("caltable = localhost:%s" % (caltable))

    if (len(caltable) > 90):
        caltableTitle = '...' + caltable[-90:]
    else:
        caltableTitle = caltable
    names = mytb.colnames()
    ant = mytb.getcol('ANTENNA1')
    fields = mytb.getcol('FIELD_ID')
    if (DEBUG):
        print("FIELD_ID column = %s" % (str(fields)))
    validFields = False
    for f in fields:
        if (f != -1):
            validFields = True
    if (validFields == False):
        print("The field_id is -1 (invalid) for all rows of this caltable.")
        print("Did you remember to run assignFieldAndScanToSolution()?")
        return()
    try:
        flags = {}
        for f in range(len(fields)):
            flags[f] = mytb.getcell('FLAG',f)
    except:
        print("No Flag column found. Are you sure this is a bandpass solution file, or is it the .ms?")
        print("If it is a solution file, does it contain solutions for both TDM and FDM spws?")
        return()

    times = mytb.getcol('TIME')
    intervals = mytb.getcol('INTERVAL')
    if ('SPECTRAL_WINDOW_ID' not in names):
        tableFormat = 33
        cal_desc_id = mytb.getcol('CAL_DESC_ID')
        VisCal = (mytb.info())['subType']
        if (VisCal == "BPOLY"):
            casalogPost(debug,"This appears to be a BPOLY cal table written in the casa 3.3/3.4 style.")
        else:
            casalogPost(debug,"This appears to be an old-format cal table from casa 3.3 or earlier.")
        if (debug): print("VisCal = %s" % (VisCal))
        mytb.close()
        ParType = "unknown"  # i.e. not Complex
        calDesc = mytb.open(caltable+'/CAL_DESC')
        originalSpws = mytb.getcol('SPECTRAL_WINDOW_ID')  # [[0,1,2,3]]
        if debug: print("originalSpws = %s" % (str(originalSpws)))
        originalSpw = originalSpws[0]                   # [0,1,2,3]
        if debug: print("originalSpw = %s" % (str(originalSpw)))
        msName = mytb.getcol('MS_NAME')[0]
        if debug: print("msName in table = %s" % (msName))
        if (vis != ''):
            msName = vis
        # This appears to be the channel range extracted from the original spw, but is
        # only present in B solutions.
        if (VisCal == "BPOLY"):
            originalChannelStart = np.zeros(len(originalSpw))
        else:
            originalChannelRange = mytb.getcol('CHAN_RANGE')
            originalChannelStart = originalChannelRange[0][0][:][0]
        mytb.close()
        try:
            mytb.open(msName+'/SPECTRAL_WINDOW')
            refFreq = mytb.getcol('REF_FREQUENCY')
            net_sideband = mytb.getcol('NET_SIDEBAND')
            measFreqRef = mytb.getcol('MEAS_FREQ_REF')
            originalSpw_casa33 = list(range(len(measFreqRef)))
            chanFreqGHz_casa33 = []     # used by showFDM
            for i in originalSpw_casa33:
                # They array shapes can vary.
                chanFreqGHz_casa33.append(1e-9 * mytb.getcell('CHAN_FREQ',i))
            mytb.close()
        except:
            print("2) Could not open the associated measurement set tables (%s). Will not translate antenna names." % (msName))
        cal_scans = None        #### added 2024Aug
    else:  # 3.4
        tableFormat = 34
        cal_desc_id = mytb.getcol('SPECTRAL_WINDOW_ID')
        cal_scans = mytb.getcol('SCAN_NUMBER')
        unique_cal_scans = np.unique(cal_scans)
        cal_scans_per_spw = {}
        for myspw in np.unique(cal_desc_id):
            cal_scans_per_spw[myspw] = np.unique(cal_scans[np.where(myspw == cal_desc_id)[0]])
            if (debug):
                print("spw %d: scans %s" % (myspw,str(cal_scans_per_spw[myspw])))
        ParType = mytb.getkeyword('ParType')    # string = 'Complex'
        msName = mytb.getkeyword('MSName')
        VisCal = mytb.getkeyword('VisCal')      # string = 'B TSYS'
        PolBasis = mytb.getkeyword('PolBasis')  # string = 'LINEAR'
        spectralWindowTable = mytb.getkeyword('SPECTRAL_WINDOW').split()[1]
        antennaTable = mytb.getkeyword('ANTENNA').split()[1]
        fieldTable = mytb.getkeyword('FIELD').split()[1]
        mytb.close()
        mytb.open(spectralWindowTable)
        chanFreqGHz = []
        originalSpws = list(range(len(mytb.getcol('MEAS_FREQ_REF'))))
        originalSpw = originalSpws  # may need to do a global replace of this
        originalSpwNames = mytb.getcol('NAME')
        for i in originalSpws:
            # They array shapes can vary.
            chanFreqGHz.append(1e-9 * mytb.getcell('CHAN_FREQ',i))
        mytb.close()
        #      CAS-6801 changes
        mytb.open(antennaTable)
        msAnt = mytb.getcol('NAME')
        mytb.close()
        mytb.open(fieldTable)
        msFields = mytb.getcol('NAME')
        mytb.close()

    if (VisCal == 'K Jones'):
        delay = True
        showpoints = True
        ampmarkstyle = '.'
        ampmarkstyle2 = 'o'
        if (markersize < 8): markersize = 8
    else:
        delay = False
    # Now open the associated ms tables via msmd tool
#     msAnt = []  # comment this out when CAS-6801 changes are in place
    if (debug): print( "creating msmd tool")
    if (ctsys.compare_version('<',[4,1,0])):
        print("This version of casa is too old to use the msmd tool.  Use au.plotbandpass instead.")
        return
    mymsmd = ''
    observatoryName = ''
    if (debug): print( "msName = %s." % (msName))
    if (os.path.exists(msName) or os.path.exists(os.path.dirname(caltable)+'/'+msName)):
        if (os.path.exists(msName) == False):
            msName = os.path.dirname(caltable)+'/'+msName
            if (debug): print( "found msName = %s." % (msName))
        if (ctsys.compare_version('<',[4,1,0])):
            print("This version of casa is too old to use the msmd tool.  Use au.plotbandpass instead.")
            return
        try:
            if (debug): print("Running mymsmd on %s..." % (msName))
            mymsmd = msmetadata()
            mymsmd.open(msName)  # this is the only open (vis not specified, but it exists)
            donetime = time.time()
            if (debug): print("%.1f sec elapsed" % (donetime-mytimestamp))
            mytimestamp = time.time()
            if (debug): print("time = %s" % (str(mytimestamp)))
            msAnt = mymsmd.antennanames(list(range(mymsmd.nantennas())))
            if (debug): print("msAnt = %s" % (str(msAnt)))
#            msFields = mymsmd.namesforfields(range(mymsmd.nfields())) # bombs if split has been run on subset of fields
            msFields = mymsmd.namesforfields()
            observatoryName = mymsmd.observatorynames()[0]
            casalogPost(debug,"Available antennas = %s" % (str(msAnt)))
        except:
            print("1)Could not open the associated measurement set tables (%s). Will not translate antenna names or frequencies." % (msName))
            return
    else:
        if (vis=='' and tableFormat < 34):
            print("Could not find the associated measurement set (%s). Will not translate antenna names or frequencies." % (msName))
        elif (vis != ''):
            # Use the ms name passed in from the command line
            msName = vis
# #          print("************* 2) Set msName to %s" % (msName))
            try:
                mymsmd = msmetadata()
                if (debug): print("Running msmd.open on %s" % (msName))
                mymsmd.open(msName) # this is the only open (vis specified)
                donetime = time.time()
                if (debug): print("%.1f sec elapsed" % (donetime-mytimestamp))
                mytimestamp = time.time()
                msAnt = mymsmd.antennanames(list(range(mymsmd.nantennas())))
#                msFields = mymsmd.namesforfields(range(mymsmd.nfields())) # bombs if split has been run on subset of fields
                msFields = mymsmd.namesforfields()
                observatoryName = mymsmd.observatorynames()[0]
                casalogPost(debug,"Available antennas = %s" % (str(msAnt)))
            except:
                print("1b) Could not open the associated measurement set tables (%s). Will not translate antenna names or channels to frequencies." % (msName))
    msFound =  False
    if (len(msAnt) > 0):
        msFound = True
        casalogPost(debug,"Fields in ms  = %s" % (str(msFields)))
    else:
        msFields = []
    if (tableFormat == 33 and msFound):  # casa 3.3
        # Now open the associated ms tables via ValueMapping to figure out channel freqs
        chanFreqGHz = []
        for ictr in range(len(originalSpw)):
            if debug: print("ictr = %d" % (ictr))
            if debug: print("nspw = %d, np.max(originalSpw) = %d" % (getnspw(mymsmd),np.max(originalSpw)))
            if (getnspw(mymsmd) < np.max(originalSpw)): # waiting on CAS-4285
                # Then there was an extra split
                i = ictr
            else:
                i = originalSpw[ictr]
            nchan = mymsmd.nchan(i)
            if (nchan > 1):
                missingFrequencyWidth = originalChannelStart[ictr]*(mymsmd.chanfreqs(i)[-1]-mymsmd.chanfreqs(i)[0])/(nchan-1)
            else:
                missingFrequencyWidth = 0
            if (missingFrequencyWidth > 0):
                if (DEBUG):
                    print("Correcting for channels flagged prior to running bandpass by %f GHz" % (missingFrequencyWidth*1e-9))
            newfreqs = 1e-9*(mymsmd.chanfreqs(i)) + missingFrequencyWidth*1e-9
            if debug: print("Appending onto chanFreqGHz: %s" % (str(newfreqs)))
            chanFreqGHz.append(newfreqs)

    # the sort order of this variable is based on tb.getcol('SPECTRAL_WINDOW_ID') which is usually (always?) in increasing order
    uniqueSpwsInCalTable = np.unique(cal_desc_id)

    # initial calculation for final message if not all spws appear with overlay='antenna'
    uniqueTimes = sloppyUnique(np.unique(times), 1.0)
    nUniqueTimes = len(uniqueTimes)
    if (nUniqueTimes == 1):
        solutionTimeSpread = 0
    else:
        solutionTimeSpread = np.max(uniqueTimes)-np.min(uniqueTimes)
    casalogPost(debug,"Found solutions with %d unique times across all spws and fields (within a threshold of 1.0 second)." % (nUniqueTimes))

    casalogPost(True,"Median difference between solution times = %f sec" % (np.median(np.diff(uniqueTimes))))
    if cal_scans is None or VisCal == 'SDSKY_PS':                                    ### added 2024Aug
        # this will show the spectrum for every solution / integration (appropriate for old Tsys tables and SDsky spectra)
        uniqueTimes = sloppyUnique(np.unique(times), solutionTimeThresholdSeconds)   ### indented 2024Aug
    else:                                                                            ### added 2024Aug
        # this will show one spectrum per scan (appropriate for Tsys tables in CASA 3.4 onward)
        uniqueTimes = sloppyUnique(times, solutionTimeThresholdSeconds, cal_scans)   ### added 2024Aug
    nUniqueTimes = len(uniqueTimes)
    if (nUniqueTimes == 1):
        casalogPost(debug,"Found solutions with %d unique time (within a threshold of %d seconds)." % (nUniqueTimes,solutionTimeThresholdSeconds))
    else:
        casalogPost(debug,"Found solutions with %d unique times (within a threshold of %d seconds)." % (nUniqueTimes,solutionTimeThresholdSeconds))

    scansForUniqueTimes = []
    if (tableFormat >= 34):
        if (len(unique_cal_scans) == 1):
            casalogPost(debug,"Found solutions with %d unique scan number %s" % (len(unique_cal_scans), str(unique_cal_scans)))
        else:
            casalogPost(debug,"Found solutions with %d unique scan numbers %s" % (len(unique_cal_scans), str(unique_cal_scans)))

        scansForUniqueTimes, nUniqueTimes = computeScansForUniqueTimes(uniqueTimes, cal_scans, times, unique_cal_scans)
    elif (scans != ''):
        print("Selection by scan is not support for old-style tables that do not have the scan number filled.")
        return
    uniqueTimesCopy = uniqueTimes[:]

    mystring = ''
    if (debug):
       for u in uniqueTimes:
           mystring += '%.6f, ' % (u)
       print(mystring)
    uniqueAntennaIds = np.unique(ant)
    uniqueFields = np.unique(fields)
    if (debug): print("uniqueFields = %s" % (str(uniqueFields)))
    nFields = len(uniqueFields)
    spwlist = []
    uniqueTimesPerFieldPerSpw = []
    for s in uniqueSpwsInCalTable:
        uniqueTimesPerField = []
        for f in uniqueFields:
            timelist = []
            for row in range(len(fields)):
                if (fields[row] == f and cal_desc_id[row] == s):
                    if (sloppyMatch(times[row], timelist, solutionTimeThresholdSeconds) == False):
                        # if this time is not already in the list for this spw/field combination, then append it
                        timelist.append(times[row])
                        spwlist.append(cal_desc_id)
            uniqueTimesPerField.append(timelist)
        uniqueTimesPerFieldPerSpw.append(uniqueTimesPerField)

    if (debug): print("about to call casalogPost")
    casalogPost(debug,displayTimesArray([[uniqueTimes]]))

    # Parse the spws to plot from the command line
    if (spw==''):
       spwsToPlot = uniqueSpwsInCalTable
    else:
       if (type(spw) == str):
             if (spw.find('!')>=0):
                   print("The ! modifier is not (yet) supported")
                   return()
             tokens = spw.split(',')
             spwsToPlot = []
             for token in tokens:
                   if (len(token) > 0):
                         if (token.find('*')>=0):
                               spwsToPlot = uniqueSpwsInCalTable
                               break
                         elif (token.find('~')>0):
                               (start,finish) = token.split('~')
                               spwsToPlot +=  list(range(int(start),int(finish)+1))
                         else:
                               spwsToPlot.append(int(token))
       elif (type(spw) == list):
           spwsToPlot = np.sort(spw)
       else:
           spwsToPlot = [spw]
    # note that spwsToPlot will not necessarily be in increasing order, e.g. if the user specified them out-of-order

    casalogPost(debug,"%d spw%s in the solution = %s" % (len(uniqueSpwsInCalTable), plural(uniqueSpwsInCalTable), str(uniqueSpwsInCalTable)))
    keepSpwsToPlot = spwsToPlot[:]
    for myspw in spwsToPlot:
        if (myspw not in uniqueSpwsInCalTable):
            print("WARNING: spw %d is not in the solution. Removing it from the list to plot." % (myspw))
            print("Available spws = ", uniqueSpwsInCalTable)
            keepSpwsToPlot.remove(myspw)
            if (ctsys.compare_version('>=',[4,1,0]) and mymsmd != ''):
# #              nonwvrspws = list(set(range(mymsmd.nspw())).difference(set(mymsmd.wvrspws())))
                if (myspw not in list(range(mymsmd.nspw()))):
                    print("FATAL: spw %d is not even in the ms.  There might be a bug in your script." % (myspw))
                    return
                elif (myspw in mymsmd.wvrspws()):
                    print("WARNING: spw %d is a WVR spw." % (myspw))
                    return
    spwsToPlot = keepSpwsToPlot[:]
    if (len(spwsToPlot) == 0):
        print("FATAL: no spws to plot")
        return
    originalSpwsToPlot = computeOriginalSpwsToPlot(spwsToPlot, originalSpw, tableFormat, debug)

    # Now generate the list of minimal basebands that contain the spws to be plotted
    if (ctsys.compare_version('>=',[4,1,0]) and msFound):
        allBasebands = []
        if (mymsmd != ''):
          try:
            for spw in originalSpwsToPlot:
                mybaseband = mymsmd.baseband(spw)
                if (debug): print("appending: spw=%d -> bb=%d" % (spw,mybaseband))
                allBasebands.append(mybaseband)
            allBasebands = np.unique(allBasebands)
            basebandDict = getBasebandDict(msName,caltable=caltable,mymsmd=mymsmd)  # needed later by showFDM()
          except:
            basebandDict = {}
            print("This dataset (%s) does not have a BBC_NO column in the SPECTRAL_WINDOW_TABLE." % (msName))
        else:
            basebandDict = {}
            telescopeName = getTelescopeNameFromCaltable(caltable)
            print("Measurement set not found.")
        if (basebandDict == {}):
            if (overlay.find('spw') >= 0):
                print("As such, since the ms cannot be found, overlay='spw' is not supported, but overlay='baseband' should work.")
                return
            elif (showfdm):
                print("As such, since the ms cannot be found, showfdm=True is not supported.")
                return
            elif (showBasebandNumber):
                print("As such, since the ms cannot be found, showBasebandNumber=True is not supported.")
                return
    elif (msFound==False):
        allBasebands = [1,2,3,4]
    else:
        basebandDict = getBasebandDict(msName,caltable=caltable,mymsmd=mymsmd)  # needed later by showFDM()
        allBasebands = []
        for spw in originalSpwsToPlot:
            mybaseband = [key for key in basebandDict if spw in basebandDict[key]]
            if (len(mybaseband)>0): allBasebands.append(mybaseband[0])
        allBasebands = np.unique(allBasebands)
        if (allBasebands == []):
            allBasebands = [1,2,3,4]
    if (debug):
        print("================ allBasebands = ", allBasebands)

    if (basebands == None or basebands == [] or basebands == ''):
        basebands = allBasebands
    elif (type(basebands) == str):
        basebands = [int(s) for s in basebands.split(',')]
    elif (type(basebands) != list):
        # it is a single integer
        basebands = [basebands]
    for baseband in basebands:
        if (baseband not in allBasebands):
            print("Baseband %d is not in the dataset (only %s)" % (baseband,str(allBasebands)))
            return

    if (msFound):
        msFieldsList = str(np.array(msFields)[uniqueFields])
    else:
        msFieldsList = 'unknown'
    casalogPost(debug,"%d field(s) in the solution = %s = %s" % (len(uniqueFields), uniqueFields,msFieldsList))

    # Figure out which kind of Bandpass solution this is.
    bOverlay = False  # Am I trying to overlay a second B-type solution?
    if (os.path.exists(caltable) == False):
          print("Caltable does not exist = %s" % (caltable))
          return()
    try:
        ([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP, uniqueTimesBP,
# #        nPolarizations,
          frequencyLimits, increments, frequenciesGHz, polynomialPhase,
          polynomialAmplitude, timesBP, antennasBP, cal_desc_idBP, spwBP]) = openBpolyFile(caltable,debug)
        bpoly = True
        bpolyOverlay = bpolyOverlay2 = False
        if (xaxis.find('chan') >= 0):
            print("Sorry, but BPOLY solutions cannot be plotted with xaxis='chan'. Proceeding with xaxis='freq'.")
            xaxis = 'freq'
        if (chanrange[0] != 0 or chanrange[1] != 0 or chanrangePercent != None):
            print("The chanrange parameter only applies if the first caltable is a B solution, not a BPOLY.")
            return()
        if (len(caltable2) > 0):
            try:
                # figure out if the next file is a BPOLY or another B solution to pick the proper error message.
                ([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP, uniqueTimesBP,
# #                nPolarizations,
                  frequencyLimits, increments, frequenciesGHz, polynomialPhase,
                  polynomialAmplitude, timesBP, antennasBP, cal_desc_idBP, spwBP]) = openBpolyFile(caltable2,debug)
                print("Sorry, but you cannot overlay two BPOLY solutions (unless caltable is a B solution and caltable2 and 3 are BPOLYs).")
            except:
                print("Sorry, but for overlays, caltable must be a B solution, whlie caltable2 and 3 can be either type.")
            return()
    except:
        casalogPost(debug,"This is a %s solution." % (VisCal))
        bpoly = bpolyOverlay = bpolyOverlay2 = False

        # Now check if there is a second file to overlay
        if (len(caltable2) > 0):
          if (os.path.exists(caltable2) == False):
                print("Caltable2 does not exist = %s" % (caltable2))
                return()
          try:
            # figure out if the next file is a BPOLY or another B solution
            ([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP, uniqueTimesBP,
# #            nPolarizations,
              frequencyLimits, increments, frequenciesGHz, polynomialPhase,
              polynomialAmplitude, timesBP, antennasBP, cal_desc_idBP, spwBP]) = openBpolyFile(caltable2,debug)
            bpolyOverlay = True
            casalogPost(debug,"Overlay the BPOLY solution")
            if (xaxis.find('chan')>=0):
                print("Sorry, but overlap of BPOLY is currently possible only with xaxis='freq'")
                return()
            if (len(caltable3) > 0):
               if (os.path.exists(caltable3) == False):
                     print("Caltable3 does not exist = %s" % (caltable3))
                     return()
               bpolyOverlay2 = True
               casalogPost(debug,"Overlay the second BPOLY solution")
               ([polyMode2, polyType2, nPolyAmp2, nPolyPhase2, scaleFactor2, nRows2, nSpws2,
                 nUniqueTimesBP2, uniqueTimesBP2,
# #               nPolarizations2,
                 frequencyLimits2, increments2, frequenciesGHz2, polynomialPhase2,
                 polynomialAmplitude2, timesBP2, antennasBP2, cal_desc_idBP2, spwBP2]) = openBpolyFile(caltable3,debug)
          except:
              # this is another B solution
              casalogPost(debug,"Overlay another %s solution" % (VisCal))
              bOverlay = True
              if (xaxis.find('freq')<0):
                    print("Currently, you must use xaxis='freq' to overlay two B solutions.")
                    return()
              if (len(caltable3) > 0):
                    print("You cannot overlay caltable3 because caltable2 is a B solution.")
                    return()
        elif (len(caltable3) > 0):
            print("You cannot have a caltable3 argument without a caltable2 argument.")
            return()

    if (overlay.find('antenna')>=0):
        overlayAntennas = True
        if (bpoly == True):
              print("The overlay of times or antennas is not supported with BPOLY solutions")
              return()
        if (len(caltable2)>0):
              print("The overlay of times or antennas not supported when overlaying a B or BPOLY solution")
              return()
        casalogPost(debug,"Will overlay solutions from different antennas")
    else:
        overlayAntennas = False

    if (overlay.find('time')>=0):
        overlayTimes = True
        if (bpoly == True):
              print("The overlay of times or antennas is not supported with BPOLY solutions")
              return()
        if (len(caltable2)>0):
              print("The overlay of times or antennas not supported when overlaying a B or BPOLY solution")
              return()
        casalogPost(debug,"Will overlay solutions from different times")
    else:
        overlayTimes = False

    if (overlay.find('spw')>=0):
        if (tableFormat < 34):
            print("Overlay spw may not work reliably for old cal tables")
        overlaySpws = True
        if (bpoly == True):
              print("The overlay of times, antennas, or spws is not supported with BPOLY solutions")
              return(vm)
        if (len(caltable2)>0):
              print("The overlay of times, antennas, or spws not supported when overlaying a B or BPOLY solution")
              return(vm)
        casalogPost(debug,"Will overlay solutions from different spws within a baseband")
    else:
        overlaySpws = False

    if (overlay.find('baseband')>=0):
        if (tableFormat < 34):
            print("Overlay baseband may not work reliably for old cal tables")
        overlayBasebands = True
        if (bpoly == True):
              print("The overlay of times, antennas, spws, or basebands is not supported with BPOLY solutions")
              return(vm)
        if (len(caltable2)>0):
              print("The overlay of times, antennas, spws, or basebands not supported when overlaying a B or BPOLY solution")
              return(vm)
        casalogPost(debug,"Will overlay solutions from all spws regardless of baseband")
    else:
        overlayBasebands = False

    if (bOverlay):
          # Now open the Bandpass solution table
          try:
                mytb.open(caltable2)
          except:
                print("Could not open the second caltable = %s" % (caltable2))
                return()
          names = mytb.colnames()
          ant2 = mytb.getcol('ANTENNA1')
          fields2 = mytb.getcol('FIELD_ID')
          times2 = mytb.getcol('TIME')
          if ('SPECTRAL_WINDOW_ID' not in names):
              if ('SNR' not in names):
                  print("This does not appear to be a cal table.")
                  return()
              else:
                  tableFormat2 = 33
                  casalogPost(debug,"This appears to be an old-format cal table from casa 3.3 or earlier.")
                  cal_desc_id2 = mytb.getcol('CAL_DESC_ID')
                  VisCal2 = (mytb.info())['subType']
                  mytb.close()
                  ParType = "unknown"  # i.e. not Complex
                  calDesc2 = mytb.open(caltable2+'/CAL_DESC')
                  originalSpws2 = mytb.getcol('SPECTRAL_WINDOW_ID')  # [[0,1,2,3]]
                  originalSpw2 = originalSpws2[0]                   # [0,1,2,3]
                  msName2 = mytb.getcol('MS_NAME')[0]
                  mytb.close()
                  # Now open the associated ms tables via ValueMapping to figure out channel freqs
                  chanFreqGHz2 = []
                  for ictr in range(len(originalSpw2)):
                      if debug: print("ictr = %d" % (ictr))
                      if debug: print("nspw = %d, np.max(originalSpw) = %d" % (getnspw(mymsmd),np.max(originalSpw2)))
                      if (getnspw(mymsmd) < np.max(originalSpw2)):
                          # Then there was an extra split
                          i = ictr
                      else:
                          i = originalSpw2[ictr]
                      nchan = mymsmd.nchan(i)
                      if (nchan > 1):
                          missingFrequencyWidth = originalChannelStart[ictr]*(mymsmd.chanfreqs(i)[-1]-mymsmd.chanfreqs(i)[0])/(nchan-1)
                      else:
                          missingFrequencyWidth = 0
                      if (missingFrequencyWidth > 0):
                          if (DEBUG):
                              print("Correcting for channels flagged prior to running bandpass by %f GHz" % (missingFrequencyWidth*1e-9))
                      newfreqs = 1e-9*(mymsmd.chanfreqs(i)) + missingFrequencyWidth*1e-9
                      if debug: print("Appending onto chanFreqGHz2: %s" % (str(newfreqs)))
                      chanFreqGHz2.append(newfreqs)
          else:
              tableFormat2 = 34
              cal_desc_id2 = mytb.getcol('SPECTRAL_WINDOW_ID')
              msName2 = mytb.getkeyword('MSName')
              ParType2 = mytb.getkeyword('ParType')    # string = 'Complex'
              VisCal2 = mytb.getkeyword('VisCal')      # string = 'B TSYS'
              PolBasis2 = mytb.getkeyword('PolBasis')  # string = 'LINEAR'
              spectralWindowTable2 = mytb.getkeyword('SPECTRAL_WINDOW').split()[1]
              mytb.close()
              mytb.open(spectralWindowTable2)
              chanFreqGHz2 = []
              originalSpws2 = list(range(len(mytb.getcol('MEAS_FREQ_REF'))))
              for i in originalSpws2:
                  # The array shapes can vary.
                  chanFreqGHz2.append(1e-9 * mytb.getcell('CHAN_FREQ',i))
              originalSpws2 = list(range(len(mytb.getcol('MEAS_FREQ_REF'))))
              originalSpw2 = originalSpws2  # may want to do a global replace of this <----------------------------------

          uniqueSpwsInCalTable2 = np.unique(cal_desc_id2)
          mytb.open(caltable2)
          try:
              flags2 = {}
              for f in range(len(fields2)):
                  flags2[f] = mytb.getcell('FLAG',f)
          except:
              print("bOverlay: No Flag column found. Are you sure this is a bandpass solution file, or is it the .ms?")
              print("If it is a solution file, does it contain solutions for both TDM and FDM spws?")
              return()
          uniqueTimes2 = sloppyUnique(np.unique(times2), solutionTimeThresholdSeconds)
          nUniqueTimes2 = len(uniqueTimes2)
# #        print("Found %d solutions in time: MJD seconds = " % (nUniqueTimes2), uniqueTimes2)
          spacing = ''
          for i in range(1,nUniqueTimes2):
              spacing += '%.0f, ' % (np.abs(uniqueTimes2[i]-uniqueTimes2[i-1]))
          casalogPost(debug,"Found %d solutions in time, spaced by seconds: %s" % (nUniqueTimes2, str(spacing)))
          casalogPost(debug,displayTimesArray([[uniqueTimes2]]))
          uniqueAntennaIds2 = np.unique(ant2)
          uniqueFields2 = np.unique(fields2)
          nFields2 = len(uniqueFields2)

          casalogPost(debug,"(boverlay) original unique spws in the second dataset = %s" % (str(np.unique(originalSpw2))))

          uniqueTimesPerFieldPerSpw2 = []
          for s in uniqueSpwsInCalTable2:
            uniqueTimesPerField2 = []
            for f in uniqueFields2:
                timelist2 = []
                for row in range(len(fields2)):
                      if (fields2[row] == f and cal_desc_id2[row] == s):
                            if (sloppyMatch(times2[row], timelist2, solutionTimeThresholdSeconds) == False):
                                  timelist2.append(times2[row])
                uniqueTimesPerField2.append(timelist2)
            uniqueTimesPerFieldPerSpw2.append(uniqueTimesPerField2)
          casalogPost(debug,"uniqueTimesPerFieldPerSpw2 = %s" % (displayTimesArray(uniqueTimesPerFieldPerSpw2)))
          casalogPost(debug,"%d spw%s in the second solution = %s" % (len(uniqueSpwsInCalTable2), plural(uniqueSpwsInCalTable2), str(uniqueSpwsInCalTable2)))
          if (msFound):
              msFieldsList = str(np.array(msFields)[uniqueFields2])
          else:
              msFieldsList = 'unknown'
          casalogPost(debug,"%d field(s) in the solution = %s = %s" % (len(uniqueFields2), uniqueFields2, msFieldsList))

    # Parse the timeranges field from the command line
    if timeranges != '':    # CAS-8439
        timerangesWasSpecified = True
    else:
        timerangesWasSpecified = False
    if (type(timeranges) == str):
        # a list of antenna numbers was given
        tokens = timeranges.split(',')
        timerangeList = []
        removeTime = []
        for token in tokens:
            if (len(token) > 0):
                if (token.find('!')==0):
                   timerangeList = list(range(len(uniqueTimes)))
                   removeTime.append(int(token[1:]))
                elif (token.find('~')>0):
                    (start,finish) = token.split('~')
                    timerangeList +=  list(range(int(start),int(finish)+1))
                else:
                    timerangeList.append(int(token))
        timerangeList = np.array(timerangeList)
        for rt in removeTime:
            timerangeList = timerangeList[np.where(timerangeList != rt)[0]]
        timerangeList = list(timerangeList)
        if (len(timerangeList) < 1):
            if (len(removeTime) > 0):
                print("Too many negated timeranges -- there are none left to plot.")
                return
            else:
                # then a blank list was specified
                timerangeList = range(len(uniqueTimes))
    elif (type(timeranges) == list):
        # it's already a list of integers
        timerangeList = timeranges
    else:
        # It's a single, integer entry
        timerangeList = [timeranges]
    if (timerangesWasSpecified and scans != ''):  # CAS-8489
        if (type(scans) == list or type(scans) == np.ndarray):
            myscan = int(scans[0])
        else:
            myscan = int(str(scans).split(',')[0])
        if (myscan not in scansForUniqueTimes):
            print(("No rows for scan %d, only " % (myscan), np.unique(scansForUniqueTimes)))
            return
        timerangeOffset = scansForUniqueTimes.index(myscan)
        timerangeList = np.array(timerangeList) + timerangeOffset
        if (debug): print(("Since both timeranges and scans was specified, generated new effective timerangeList: ", timerangeList))
    if (max(timerangeList) >= len(uniqueTimes)):
        print("Invalid timerange.  Solution has %d times (%d~%d)" % (len(uniqueTimes),0,len(uniqueTimes)-1))
        return
    timerangeListTimes = np.array(uniqueTimes)[timerangeList]
    if debug:
        print("%d timerangeListTimes to be plotted" % (len(timerangeListTimes)))
    timerangeListTimesString = mjdsecArrayToUTString(timerangeListTimes)
    if (tableFormat == 33 or scansForUniqueTimes == []):
        # SMA data with scan numbers of -1 has empty list for scansForUniqueTimes
        scansToPlot = []
        if (scans != ''):
            print("Selection by scan is not possible for this dataset.")
            return
    else:
        if (debug): print("A)scansForUniqueTimes = %s" % (str(scansForUniqueTimes)))
        scansToPlot = np.array(scansForUniqueTimes)[timerangeList]
        if (np.unique(scansToPlot)[0] == -1):
            # scan numbers are not correct in this new-style cal table
            scansToPlot = []
            if (scans != ''):
                print("Selection by scan number is not possible with this dataset.")
                return
        if (scans != '' and scans != []):
            if (type(scans) == list):
                scansToPlot = scans
            elif (type(scans) == str):
                scansToPlot = [int(a) for a in scans.split(',')]
            else:
                scansToPlot = [scans]
            for scan in scansToPlot:
                if (scan not in scansForUniqueTimes):
                    print("Scan %d is not in any solution" % (scan))
                    return
        if debug: print("scans to plot: %s" % (str(scansToPlot)))
    scansToPlotPerSpw = {}
    casalogPost(debug,"originalSpwsToPlot: %s" % (originalSpwsToPlot))
#    for myspw in np.unique(cal_desc_id):       ### removed 2024Aug
    for myspw in originalSpwsToPlot:            ### added 2024Aug
        scansToPlotPerSpw[myspw] = []
    if tableFormat == 34 and scansForUniqueTimes != []:                ### added 2024Aug
        scansToPlotRevised = []                    ### added 2024Aug
        timerangeListTimesRevised = []             ### added 2024Aug
        for scan in scansToPlot:                   ### indented 2024Aug
#        for myspw in np.unique(cal_desc_id):      ### removed 2024Aug
            for myspw in originalSpwsToPlot:         ### added 2024Aug
                if (scan in cal_scans_per_spw[myspw]):    ### indented 2024Aug
                    scansToPlotPerSpw[myspw].append(scan) ### indented 2024Aug
                    scansToPlotRevised.append(scan)       #### added 2024Aug
                    idx = np.where(cal_scans == scan)     #### added 2024Aug
                    timerangeListTimesRevised += list(np.unique(times[idx])) #### added 2024Aug
        scansToPlot = np.unique(scansToPlotRevised)                    #### added 2024Aug
#        timerangeListTimes = np.unique(timerangeListTimesRevised)      #### added 2024Aug
        timerangeListTimes = np.array(uniqueTimes)[timerangeList]      #### added 2024Aug after the Aug22 benchmark
        casalogPost(debug, "%d timerangeListTimes (after filtering for spw) = %s" % (len(timerangeListTimes), timerangeListTimes))      #### add 2024Aug08
        casalogPost(debug, "scansToPlot (after filtering for spw) = %s" % (scansToPlot))      #### added 2024Aug

    # remove spws that do not have any scans to be plotted
    # but only for tables that have a scan number column, and not filled with all -1
    if (tableFormat > 33 and scansForUniqueTimes != []):
        for myspw in np.unique(cal_desc_id):
            if myspw in scansToPlotPerSpw:   #### added 2024Aug
                if (scansToPlotPerSpw[myspw] == []):              #### indented 2024Aug
                    indexDelete = np.where(spwsToPlot==myspw)[0]  #### indented 2024Aug
                    if (len(indexDelete) > 0):                    #### indented 2024Aug
                        spwsToPlot = np.delete(spwsToPlot, indexDelete[0])  #### indented 2024Aug
                elif (debug):
                    print("scans to plot for spw %d: %s" % (myspw, scansToPlotPerSpw[myspw]))
        print("spws to plot (sorted) = ", sorted(spwsToPlot))
    casalogPost(debug,"scans to plot: %s" % (str(scansToPlot)))
    casalogPost(debug,"UT times to plot: %s" % (timerangeListTimesString))
    casalogPost(debug,"Corresponding time IDs (0-based): %s" % (str(timerangeList)))
    if (len(timerangeListTimes) > len(np.unique(scansToPlot))):
        # fix for CAS-9474
        uniqueScansToPlot, idx = np.unique(scansToPlot, return_index=True)
        if (len(uniqueScansToPlot) < len(scansToPlot)):
            # If the solution time for one spw differs by more than solutionTimeThresholdSeconds from
            # another spw, then we will get 2 identical entries for the same scan, and thus duplicate
            # plots.  So, remove one.
            if debug: print("Engaging fix for CAS-9474")
            scansToPlot = uniqueScansToPlot
            timerangeListTimes = list(np.array(timerangeListTimes)[idx])
            timerangeList = list(np.array(timerangeList)[idx])
            timerangeListTimesString = mjdsecArrayToUTString(timerangeListTimes)
            casalogPost(debug,"Revised scans to plot: %s" % (str(scansToPlot)))
            casalogPost(debug,"Revised UT times to plot: %s" % (timerangeListTimesString))
            casalogPost(debug,"Corresponding time IDs (0-based): %s" % (str(timerangeList)))

    # Reassign overlay colors list if time overlays use a single color
    overlayColors = overlayColorsList
    if (overlayTimes and not overlayAntennas and len(timerangeList) > 1):
        timeOverlayColors = [overlayColors[idx] for idx in timerangeList]
        uniqueTimeColors = np.unique(timeOverlayColors)
        if (len(uniqueTimeColors) == 1):
            # Redo overlay colors list with shift
            if debug: print("Shifting overlay colors list to avoid repeats")
            shiftOverlayColors = overlayColorsSequence
            while len(shiftOverlayColors) <= timerangeList[-1]:
                 shiftOverlayColors += shiftOverlayColors[1:]
            overlayColors = shiftOverlayColors

    # Check for mismatch
    if (bpolyOverlay):
        if (len(timerangeListTimes) > nUniqueTimesBP):
            print("There are more timeranges (%d) to plot from %s than exist in the caltable2=%s (%d)" % (len(timerangeListTimes), caltable,caltable2, nUniqueTimesBP))
            for i in timerangeList:
                if (sloppyMatch(timerangeListTimes[i],uniqueTimesBP[0],
                                solutionTimeThresholdSeconds, mytime,
                                scansToPlot, scansForUniqueTimes, myprint=False)):
                    print("Try adding 'timeranges=%d'" % (i+1))
            return()
        if (bpolyOverlay2):
            if (len(timerangeListTimes) > nUniqueTimesBP2):
                print("There are more timeranges to plot (%d) from %s than exist in the caltable3=%s (%d)" % (len(timerangeListTimes), caltable, caltable3, nUniqueTimesBP2))
                return()

    # Parse the antenna string to emulate plotms
    if (type(antenna) == str):
       if (len(antenna) == sum([m in myValidCharacterListWithBang for m in antenna])):
           # a simple list of antenna numbers was given
           tokens = antenna.split(',')
           antlist = []
           removeAntenna = []
           for token in tokens:
               if (len(token) > 0):
                   if (token.find('*')==0 and len(token)==1):
                       antlist = uniqueAntennaIds
                       break
                   elif (token.find('!')==0):
                       antlist = uniqueAntennaIds
                       removeAntenna.append(int(token[1:]))
                   elif (token.find('~')>0):
                       (start,finish) = token.split('~')
                       antlist +=  list(range(int(start),int(finish)+1))
                   else:
                       antlist.append(int(token))
           antlist = np.array(antlist)
           for rm in removeAntenna:
               antlist = antlist[np.where(antlist != rm)[0]]
           antlist = list(antlist)
           if (len(antlist) < 1 and len(removeAntenna)>0):
               print("Too many negated antennas -- there are no antennas left to plot.")
               return()
       else:
           # The antenna name (or list of names) was specified
           tokens = antenna.split(',')
           if (msFound):
               antlist = []
               removeAntenna = []
               for token in tokens:
#                   if (token in mymsmd.antennanames(range(mymsmd.nantennas()))):
                   if (token in msAnt):
                       antlist = list(antlist)  # needed in case preceding antenna had ! modifier
#                       antlist.append(mymsmd.antennaids(token)[0])
                       antlist.append(list(msAnt).index(token))
                   elif (token[0] == '!'):
#                       if (token[1:] in mymsmd.antennanames(range(mymsmd.nantennas()))):
                       if (token[1:] in msAnt):
                           antlist = uniqueAntennaIds # range(mymsmd.nantennas())
#                           removeAntenna.append(mymsmd.antennaids(token[1:])[0])
                           removeAntenna.append(list(msAnt).index(token[1:]))
                       else:
                           print("Antenna %s is not in the ms. It contains: " % (token), mymsmd.antennanames(list(range(mymsmd.nantennas()))))
                           return()
                   else:
                       print("Antenna %s is not in the ms. It contains: " % (token), mymsmd.antennanames(list(range(mymsmd.nantennas()))))
                       return()
               antlist = np.array(antlist)
               for rm in removeAntenna:
                   antlist = antlist[np.where(antlist != rm)[0]]
               antlist = list(antlist)
               if (len(antlist) < 1 and len(removeAntenna)>0):
                   print("Too many negated antennas -- there are no antennas left to plot.")
                   return()
           else:
               print("Antennas cannot be specified my name if the ms is not found.")
               return()
    elif (type(antenna) == list):
        # it's a list of integers
        antlist = antenna
    else:
        # It's a single, integer entry
        antlist = [antenna]
    casalogPost(debug,"antlist = %s" % (str(antlist)))
    if (len(antlist) > 0):
       antennasToPlot = np.intersect1d(uniqueAntennaIds,antlist)
    else:
       antennasToPlot = uniqueAntennaIds
    if (len(antennasToPlot) < 2 and overlayAntennas):
        print("More than 1 antenna is required for overlay='antenna'.")
        return()
    casalogPost(debug,"antennasToPlot = %s" % (str(antennasToPlot)))

    # Parse the field string to emulate plotms
    removeField = []
    if (type(field) == str):
       if (len(field) == sum([m in myValidCharacterListWithBang for m in field])):
           casalogPost(debug,"a list of field numbers was given")
           # a list of field numbers was given
           tokens = field.split(',')
           fieldlist = []
           for token in tokens:
               if (token.find('*')>=0):
                   fieldlist = uniqueFields
                   break
               elif (token.find('!')==0):
                   fieldlist = uniqueFields
                   removeField.append(int(token[1:]))
               elif (len(token) > 0):
                   if (token.find('~')>0):
                       (start,finish) = token.split('~')
                       fieldlist +=  list(range(int(start),int(finish)+1))
                   else:
                       fieldlist.append(int(token))
           fieldlist = np.array(fieldlist)
           for rm in removeField:
               fieldlist = fieldlist[np.where(fieldlist != rm)[0]]
           fieldlist = list(fieldlist)
           if (len(fieldlist) < 1 and len(removeField)>0):
               print("Too many negated fields -- there are no fields left to plot.")
               return()
       else:
           casalogPost(debug,"The field name, or list of names was given")
           # The field name (or list of names, or wildcard) was specified
           tokens = field.split(',')
           if (msFound):
               fieldlist = []
               removeField = []
               for token in tokens:
                   myloc = token.find('*')
                   casalogPost(debug,"token=%s, myloc=%d" % (token,myloc))
                   if (myloc > 0):
                       casalogPost(debug,"Saw wildcard in the name")
                       for u in uniqueFields:
                           myFieldName = GetFieldNamesForFieldId(u, mymsmd, msFields)
                           if (token[0:myloc]==myFieldName[0:myloc]):
                               if (DEBUG):
                                   print("Found wildcard match = %s" % mymsmd.namesforfields(u))
                               fieldlist.append(u)
                           else:
                               if (DEBUG):
                                   print("No wildcard match with = %s" % mymsmd.namesforfields(u))
                   elif (myloc==0):
                       casalogPost(debug,"Saw wildcard at start of name")
                       for u in uniqueFields:
                           fieldlist.append(u)
                   elif (token in msFields):
                       fieldlist = list(fieldlist)  # needed in case preceding field had ! modifier
                       fieldlist.append(GetFieldIdsForFieldName(token, mymsmd, msFields))
                   elif (token[0] == '!'):
                       if (fieldlist == []):
                           for u in uniqueFields:
                               fieldlist.append(u)
                       if (token[1:] in msFields):
                           removeField.append(GetFieldIdsForFieldName(token[1:], mymsmd, msFields))
                       else:
                           print("Field %s is not in the ms. It contains: %s, %s" % (token, str(uniqueFields), str(np.unique(msFields))))
                           return()
                   else:
                       casalogPost(debug,"Field not in ms")
                       fieldlist = []
                       for f in mymsmd.namesforfields():
                           fieldlist.append(mymsmd.fieldsforname(f))
                       print("Field %s is not in the ms. It contains: %s, %s" % (token, str(uniqueFields), str(np.unique(msFields))))
                       return()
               fieldlist = np.array(fieldlist)
               for rm in removeField:
                   fieldlist = fieldlist[np.where(fieldlist != rm)[0]]
               fieldlist = list(fieldlist)
               if (len(fieldlist) < 1 and len(removeField)>0):
                   print("Too many negated fields -- there are no fields left to plot.")
                   return()
           else:
               print("Fields cannot be specified my name if the ms is not found.")
               return()
    elif (type(field) == list):
        # it's a list of integers
        fieldlist = field
    else:
        # It's a single, integer entry
        fieldlist = [field]

    casalogPost(debug,"fieldlist = %s" % (str(fieldlist)))

    if (len(fieldlist) > 0):
        if (DEBUG):
            print("Finding intersection of %s with %s" % (str(uniqueFields), str(fieldlist)))
        fieldsToPlot = np.intersect1d(uniqueFields,np.array(fieldlist))
        if (bOverlay):
            fieldsToPlot = np.intersect1d(np.union1d(uniqueFields,uniqueFields2),np.array(fieldlist))
        if (len(fieldsToPlot) < 1):
            print("Requested field not found in solution")
            return()
    else:
        fieldsToPlot = uniqueFields  # use all fields if none are specified
        if (bOverlay):
            fieldsToPlot = np.union1d(uniqueFields,uniqueFields2)
        if (DEBUG):
            print("bOverlay = %s" % (bOverlay))
            print("set fieldsToPlot to uniqueFields = %s" % (str(fieldsToPlot)))
    fieldIndicesToPlot = []
    casalogPost(debug,"fieldsToPlot = %s" % (str(fieldsToPlot)))

    if (showatmfield == ''):
        showatmfield = fieldsToPlot[0]
    else:
        if (str.isdigit(str(showatmfield))):
            showatmfield = int(str(showatmfield))
            if (showatmfield not in fieldsToPlot):
                print("The showatmfield (%d) is not in the list of fields to plot: %s" % (showatmfield, str(fieldsToPlot)))
                return()
        else:
            showatmfieldName = showatmfield
            showatmfield = mymsmd.fieldsforname(showatmfield)
            if (list(showatmfield) == []):
                print("The showatmfield (%s) is not in the ms." %(showatmfieldName))
                return()
            if (type(showatmfield) == type(np.ndarray(0))):
                # more than one field IDs exist for this source name, so pick the first
                showatmfield = showatmfield[0]
            if (showatmfield not in fieldsToPlot):
                print("The showatmfield (%d=%s) is not in the list of fields to plot: %s" % (showatmfield, showatmfieldName, str(fieldsToPlot)))
                return()

    for i in fieldsToPlot:
        match = np.where(i==uniqueFields)[0]
        if (len(match) < 1 and bOverlay):
            match = np.where(i==uniqueFields2)[0]
        fieldIndicesToPlot.append(match[0])

    casalogPost(debug,"spws to plot (sorted) = %s" % (str(sorted(spwsToPlot))))
    casalogPost(debug,"Field IDs to plot: %s" % (str(fieldsToPlot)))

    redisplay = False
    myap = 0  # this variable is necessary to make the 'b' option work for
              # subplot=11, yaxis=both.  It keeps track of whether 'amp' or
              # 'phase' was the first plot on the page.

    # I added pb.ion() because Remy suggested it.
    if (interactive):
        pb.ion()  # This will open a new window if not present.
    else:
        pb.ioff() # This will not destroy an existing window or prevent new plots from appearing there.
# # The call to pb.figure() causes an additional new window everytime.
# #  pb.figure()

    newylimits = [LARGE_POSITIVE, LARGE_NEGATIVE]

    safe_pb_clf() # pb.clf()
    if (bpoly):
      # The number of polarizations cannot be reliably inferred from the shape of
      # the GAIN column in the caltable.  Must use the shape of the DATA column
      # in the ms.
      if (debug): print("in bpoly")
      if (msFound):
          (corr_type, corr_type_string, nPolarizations) = getCorrType(msName, spwsToPlot, mymsmd, debug)
          casalogPost(debug,"nPolarizations in first spw to plot = %s" % (str(nPolarizations)))
      else:
          print("With no ms available, I will assume ALMA data: XX, YY, and refFreq=first channel.")
          chanFreqGHz = []
          corr_type_string = ['XX','YY']
          corr_type = [9,12]
          nPolarizations = 2
      nPolarizations2 = nPolarizations
      if (corr_type_string == []):
          return()
      polsToPlot = checkPolsToPlot(polsToPlot, corr_type_string, debug)
      if (polsToPlot == []):
          return()
      # Here we are only plotting one BPOLY solution, no overlays implemented.
      overlayAntennas = False
      # rows in the table are: antennas 0..nAnt for first spw, antennas 0..nAnt
      # for 2nd spw...
      pagectr = 0
      if debug:
          print("Setting pages to blank list")
      pages = []
      xctr = 0
      newpage = 1
      while (xctr < len(antennasToPlot)):
        xant = antennasToPlot[xctr]
        antstring, Antstring = buildAntString(xant,msFound,msAnt)
        spwctr = 0
        spwctrFirstToPlot = spwctr
        while (spwctr < len(spwsToPlot)):
         ispw = spwsToPlot[spwctr]
         mytime = 0
         while (mytime < nUniqueTimes):
           if (len(uniqueTimes) > 0 and (mytime not in timerangeList)):
               if (debug):
                   print("@@@@@@@@@@@@@@@  Skipping mytime=%d" % (mytime))
               mytime += 1
               continue
           if (newpage == 1):
              pages.append([xctr,spwctr,mytime,0])
              if debug:
                  print("top: appending [%d,%d,%d,%d]" % (xctr,spwctr,mytime,0))
              newpage = 0
           antennaString = 'Ant%2d: %s,  ' % (xant,antstring)
           for index in range(nRows):
              # Find this antenna, spw, and timerange combination in the table
              if tableFormat >= 34:                            ### added 2024Aug
                  scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
              else:                                            ### added 2024Aug
                  scansToPlotHere = scansToPlot                ### added 2024Aug
              if (xant==ant[index] and sloppyMatch(uniqueTimes[mytime],times[index],solutionTimeThresholdSeconds,
                                                   mytime, scansToPlotHere, scansForUniqueTimes,   ### modified 2024Aug
                                                   myprint=debugSloppyMatch) and
                  (ispw == cal_desc_id[index]) and (fields[index] in fieldsToPlot)):
                  fieldIndex = np.where(fields[index] == uniqueFields)[0]
                  if (type(fieldIndex) == list or type(fieldIndex) == np.ndarray):
                      fieldIndex = fieldIndex[0]
                  validDomain = [frequencyLimits[0,index], frequencyLimits[1,index]]
                  if (msFound):
                      fieldString = msFields[uniqueFields[fieldIndex]]
                  else:
                      fieldString = str(field)
                  timeString = ',  t%d/%d  %s' % (mytime,nUniqueTimes-1,utstring(uniqueTimes[mytime],3))
                  if (scansForUniqueTimes != []):
                      if (scansForUniqueTimes[mytime]>=0):
                          timeString = ',  scan%d  %s' % (scansForUniqueTimes[mytime],utstring(uniqueTimes[mytime],3))
                  if ((yaxis.find('amp')>=0 or amplitudeWithPhase) and myap==0):
                    xframe += 1
                    myUniqueColor = []
                    if (debug):
                        print("v) incrementing xframe to %d" % xframe)
                    adesc = safe_pb_subplot(xframe)
                    previousSubplot = xframe
                    if (ispw==originalSpw[ispw]):
                        # all this was added mistakenly here.  If it causes a bug, remove it.
                        if (overlayTimes and len(fieldsToPlot) > 1):
                          indices = fstring = ''
                          for f in fieldIndicesToPlot:
                              if (f != fieldIndicesToPlot[0]):
                                  indices += ','
                                  fstring += ','
                              indices += str(uniqueFields[f])
                              if (msFound):
                                  fstring += msFields[uniqueFields[f]]
                          if (len(fstring) > fstringLimit):
                              fstring = fstring[0:fstringLimit] + '...'
                          pb.title("%sspw%2d,  fields %s: %s%s" % (antennaString,ispw,
                                  indices, fstring, timeString), size=titlesize)
                        else:
                          pb.title("%sspw%2d,  field %d: %s%s" % (antennaString,ispw,
                                  uniqueFields[fieldIndex],fieldString,timeString), size=titlesize)
                    else:
                        if (overlayTimes and len(fieldsToPlot) > 1):
                          indices = fstring = ''
                          for f in fieldIndicesToPlot:
                              if (f != fieldIndicesToPlot[0]):
                                  indices += ','
                                  fstring += ','
                              indices += str(uniqueFields[f])
                              if (msFound):
                                  fstring += msFields[uniqueFields[f]]
                          if (len(fstring) > fstringLimit):
                              fstring = fstring[0:fstringLimit] + '...'
                          pb.title("%sspw%2d (%d),  fields %s: %s%s" % (antennaString,ispw,originalSpw[ispw],
                                  indices, fstring, timeString), size=titlesize)
                        else:
                          pb.title("%sspw%2d (%d),  field %d: %s%s" % (antennaString,ispw,originalSpw[ispw],
                                  uniqueFields[fieldIndex],fieldString,timeString), size=titlesize)
                    amplitudeSolutionX = np.real(scaleFactor[index])+calcChebyshev(polynomialAmplitude[index][0:nPolyAmp[index]], validDomain, frequenciesGHz[index]*1e+9)
                    amplitudeSolutionY = np.real(scaleFactor[index])+calcChebyshev(polynomialAmplitude[index][nPolyAmp[index]:2*nPolyAmp[index]], validDomain, frequenciesGHz[index]*1e+9)
                    amplitudeSolutionX += 1 - np.mean(amplitudeSolutionX)
                    amplitudeSolutionY += 1 - np.mean(amplitudeSolutionY)
                    if (yaxis.lower().find('db') >= 0):
                        amplitudeSolutionX = 10*np.log10(amplitudeSolutionX)
                        amplitudeSolutionY = 10*np.log10(amplitudeSolutionY)
                    if (nPolarizations == 1):
                        pb.plot(frequenciesGHz[index], amplitudeSolutionX, '%s%s'%(xcolor,bpolymarkstyle),markeredgewidth=markeredgewidth)
                    else:
                        pb.plot(frequenciesGHz[index], amplitudeSolutionX, '%s%s'%(xcolor,bpolymarkstyle), frequenciesGHz[index], amplitudeSolutionY, '%s%s'%(ycolor,bpolymarkstyle),markeredgewidth=markeredgewidth)
                    if (plotrange[0] != 0 or plotrange[1] != 0):
                        SetNewXLimits([plotrange[0],plotrange[1]])
                    if (plotrange[2] != 0 or plotrange[3] != 0):
                        SetNewYLimits([plotrange[2],plotrange[3]])
                    xlim=pb.xlim()
                    ylim=pb.ylim()
                    ResizeFontsSetGrid(adesc,mysize)
                    if (yaxis.lower().find('db')>=0):
                        pb.ylabel('Amplitude (dB)', size=mysize)
                    else:
                        pb.ylabel('Amplitude', size=mysize)
#                    pb.xlabel('Frequency (GHz)', size=mysize)
                    pb.xlabel('Frequency (GHz) (%d channels)'%(len(frequenciesGHz[index])), size=mysize)  #  2024Aug
                    if (xframe == firstFrame):
                        DrawBottomLegendPageCoords(msName, uniqueTimes[mytime], mysize, figfile)
                        pb.text(xstartTitle, ystartTitle,
                                '%s (degamp=%d, degphase=%d)'%(caltableTitle,nPolyAmp[index]-1,
                                nPolyPhase[index]-1),size=mysize,
                                transform=pb.gcf().transFigure)
                    # draw polarization labels
                    x0 = xstartPolLabel
                    y0 = ystartPolLabel
                    for p in range(nPolarizations):
                        if (corrTypeToString(corr_type[p]) in polsToPlot):
                            pb.text(x0, y0-0.03*subplotRows*p, corrTypeToString(corr_type[p])+'',
                                    color=pcolor[p],size=mysize, transform=pb.gca().transAxes)
                    if (xframe == 111 and amplitudeWithPhase):
                       if (len(figfile) > 0):
                           # We need to make a new figure page
                           plotfiles.append(makeplot(figfile,msFound,msAnt,
                                                     overlayAntennas,pages,pagectr,
                                                     density,interactive,antennasToPlot,
                                                     spwsToPlot,overlayTimes,overlayBasebands,
                                                     0,xant,ispw,subplot,resample,
                                                     debug,figfileSequential,figfileNumber))
                           figfileNumber += 1
                       donetime = time.time()
                       if (interactive):
                          pb.draw()
# #                        myinput = raw_input("(%.1f sec) Press return for next page (b for backwards, q to quit): "%(donetime-mytimestamp))
                          myinput = input("Press return for next page (b for backwards, q to quit): ")
                       else:
                          myinput = ''
                       skippingSpwMessageSent = 0
                       mytimestamp = time.time()
                       if (myinput.find('q') >= 0):
                           showFinalMessage(overlayAntennas, solutionTimeSpread, nUniqueTimes)
                           return()
                       if (myinput.find('b') >= 0):
                           if (pagectr > 0):
                               pagectr -= 1
                           #redisplay the current page by setting ctrs back to the value they had at start of that page
                           xctr = pages[pagectr][PAGE_ANT]
                           spwctr = pages[pagectr][PAGE_SPW]
                           mytime = pages[pagectr][PAGE_TIME]
                           myap = pages[pagectr][PAGE_AP]
                           xant = antennasToPlot[xctr]
                           antstring, Antstring = buildAntString(xant,msFound,msAnt)
                           ispw = spwsToPlot[spwctr]
                           redisplay = True
                       else:
                           pagectr += 1
                           if (pagectr >= len(pages)):
                                 pages.append([xctr,spwctr,mytime,1])
                                 print("appending [%d,%d,%d,%d]" % (xctr,spwctr,mytime,1))
                                 newpage = 0
                       safe_pb_clf()

                  if (yaxis.find('phase')>=0 or amplitudeWithPhase):
                    xframe += 1
                    myUniqueColor = []
# #                  print("w) incrementing xframe to %d" % xframe)
                    adesc = safe_pb_subplot(xframe)
                    previousSubplot = xframe
                    if (ispw==originalSpw[ispw]):
                          pb.title("%sspw%2d,  field %d: %s%s" % (antennaString,ispw,
                                 uniqueFields[fieldIndex],fieldString,timeString), size=titlesize)
                    else:
                          pb.title("%sspw%2d (%d),  field %d: %s%s" % (antennaString,ispw,originalSpw[ispw],
                                 uniqueFields[fieldIndex],fieldString,timeString), size=titlesize)
                    phaseSolutionX = calcChebyshev(polynomialPhase[index][0:nPolyPhase[index]], validDomain, frequenciesGHz[index]*1e+9) * 180/math.pi
                    phaseSolutionY = calcChebyshev(polynomialPhase[index][nPolyPhase[index]:2*nPolyPhase[index]], validDomain, frequenciesGHz[index]*1e+9) * 180/math.pi
                    if (nPolarizations == 1):
                        pb.plot(frequenciesGHz[index], phaseSolutionX, '%s%s'%(xcolor,bpolymarkstyle),markeredgewidth=markeredgewidth)
                    else:
                        pb.plot(frequenciesGHz[index], phaseSolutionX, '%s%s'%(xcolor,bpolymarkstyle), frequenciesGHz[index], phaseSolutionY, '%s%s'%(ycolor,bpolymarkstyle),markeredgewidth=markeredgewidth)
                    ResizeFontsSetGrid(adesc,mysize)
                    pb.ylabel('Phase (deg)', size=mysize)
#                    pb.xlabel('Frequency (GHz)', size=mysize)
                    pb.xlabel('Frequency (GHz) (%d channels)'%(len(frequenciesGHz[index])), size=mysize)  ### 2024Aug
                    if (plotrange[0] != 0 or plotrange[1] != 0):
                        SetNewXLimits([plotrange[0],plotrange[1]])
                    if (plotrange[2] != 0 or plotrange[3] != 0):
                        SetNewYLimits([plotrange[2],plotrange[3]])
                    if (amplitudeWithPhase and phase != ''):
                        if (phase[0] != 0 or phase[1] != 0):
                            SetNewYLimits(phase)
                    if (xframe == firstFrame):
                        pb.text(xstartTitle, ystartTitle,
                                '%s (degamp=%d, degphase=%d)'%(caltable,
                                nPolyAmp[index]-1,nPolyPhase[index]-1),
                                size=mysize, transform=pb.gcf().transFigure)
                    # draw polarization labels
                    x0 = xstartPolLabel
                    y0 = ystartPolLabel
                    for p in range(nPolarizations):
                        if (corrTypeToString(corr_type[p]) in polsToPlot):
                            pb.text(x0, y0-0.03*p*subplotRows, corrTypeToString(corr_type[p])+'',
                                    color=pcolor[p],size=mysize, transform=pb.gca().transAxes)

           # end of 'for' loop over rows
           redisplay = False
           pb.subplots_adjust(hspace=myhspace, wspace=mywspace)
           if (xframe == lastFrame):
              if (len(figfile) > 0):
                  plotfiles.append(makeplot(figfile,msFound,msAnt,
                                            overlayAntennas,pages,pagectr,
                                            density,interactive,antennasToPlot,
                                            spwsToPlot,overlayTimes,overlayBasebands,
                                            1,xant,ispw,
                                            subplot,resample,debug,
                                            figfileSequential,figfileNumber))
                  figfileNumber += 1
              donetime = time.time()
              if (interactive):
                 pb.draw()
# #               myinput = raw_input("(%.1f sec) Press return for next page (b for backwards, q to quit): "%(donetime-mytimestamp))
                 myinput = input("Press return for next page (b for backwards, q to quit): ")
              else:
                 myinput = ''
              skippingSpwMessageSent = 0
              mytimestamp = time.time()
              if (myinput.find('q') >= 0):
                  return()
              if (myinput.find('b') >= 0):
                  if (pagectr > 0):
                      pagectr -= 1
                  #redisplay the current page by setting ctrs back to the value they had at start of that page
                  xctr = pages[pagectr][PAGE_ANT]
                  spwctr = pages[pagectr][PAGE_SPW]
                  mytime = pages[pagectr][PAGE_TIME]
                  myap = pages[pagectr][PAGE_AP]
                  xant = antennasToPlot[xctr]
                  antstring, Antstring = buildAntString(xant,msFound,msAnt)
                  ispw = spwsToPlot[spwctr]
                  redisplay = True
              else:
                  pagectr += 1
                  if (pagectr >= len(pages)):
                      newpage = 1
                  else:
                      newpage = 0
              if (debug):
                  print("1)Setting xframe to %d" % xframeStart)
              xframe = xframeStart
              if (xctr+1 < len(antennasToPlot)):
                  # don't clear the final plot when finished
                  safe_pb_clf()
              if (spwctr+1<len(spwsToPlot) or mytime+1<nUniqueTimes):
                  # don't clear the final plot when finished
                  safe_pb_clf()
              pb.subplots_adjust(hspace=myhspace, wspace=mywspace)
           if (redisplay == False):
               mytime += 1
               if (debug):
                   print("Incrementing mytime to %d" % (mytime))
         # end while(mytime)
         if (redisplay == False):
             spwctr +=1
             if (debug):
                 print("Incrementing spwctr to %d" % (spwctr))
        # end while(spwctr)
        if (redisplay == False):
            xctr += 1
      # end while(xctr) for BPOLY
      if (len(figfile) > 0 and pagectr<len(pages)):
         plotfiles.append(makeplot(figfile,msFound,msAnt,overlayAntennas,pages,
                                   pagectr,density,interactive,antennasToPlot,
                                   spwsToPlot,overlayTimes,overlayBasebands,
                                   2,xant,ispw,subplot,resample,debug,
                                   figfileSequential,figfileNumber))
         figfileNumber += 1
      if (len(plotfiles) > 0 and buildpdf):
          pdfname = figfile+'.pdf'
          filelist = ''
          plotfiles = np.unique(plotfiles)
          for i in range(len(plotfiles)):
              cmd = '%s -density %d %s %s.pdf' % (convert,density,plotfiles[i],plotfiles[i].split('.png')[0])
              casalogPost(debug,"Running command = %s" % (cmd))
              mystatus = os.system(cmd)
              if (mystatus != 0):
                  break
              filelist += plotfiles[i].split('.png')[0] + '.pdf '
          if (mystatus != 0):
              print("ImageMagick is missing, no PDF created")
              buildpdf = False
          if (buildpdf):
              cmd = '%s %s cat output %s' % (pdftk, filelist, pdfname)
              casalogPost(debug,"Running command = %s" % (cmd))
              mystatus = os.system(cmd)
              if (mystatus != 0):
                  cmd = '%s -q -sPAPERSIZE=letter -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=%s %s' % (gs,pdfname,filelist)
                  casalogPost(debug,"Running command = %s" % (cmd))
                  mystatus = os.system(cmd)
              if (mystatus == 0):
                  print("PDF left in %s" % (pdfname))
                  os.system("rm -f %s" % filelist)
              else:
                  print("Both pdftk and ghostscript are missing, no PDF created")
      return()

####################################################################################
# # bpoly == false,
#################
    msFound = False
    mytb.open(caltable)
    uniqueScanNumbers = sorted(np.unique(mytb.getcol('SCAN_NUMBER')))
    if (ParType == 'Complex'):  # casa >= 3.4
        gain = {}
        for f in range(len(fields)):
            gain[f] = mytb.getcell('CPARAM',f)
    else: # casa 3.3
        gain = {}
# #      gain = mytb.getcol('FPARAM')       # 2,128,576
        if ('FPARAM' in mytb.colnames()):
            for f in range(len(fields)):
                gain[f] = mytb.getcell('FPARAM',f)
        else:
            for f in range(len(fields)):
                gain[f] = mytb.getcell('GAIN',f)
    nPolarizations =  len(gain[0])
    if (debug):
        print("(1)Set nPolarizations = %d" % nPolarizations)
    ggx = {}
    for g in range(len(gain)):
        ggx[g] =  gain[g][0]
    if (nPolarizations == 2):
        ggy = {}
        for g in range(len(gain)):
            ggy[g] =  gain[g][1]
    mytb.close()

    if (debug):
      print("nPolarizations = %s" % (str(nPolarizations)))
    nRows = len(gain)
    if (bOverlay):
          mytb.open(caltable2)
          gain2 = {}
          if (ParType == 'Complex'):
# #            gain2 = mytb.getcol('CPARAM')
              for f in range(len(fields2)):
                  gain2[f] = mytb.getcell('CPARAM',f)
          else:
# #            gain2 = mytb.getcol('FPARAM')
              for f in range(len(fields2)):
                  if (tableFormat2 == 34):
                      gain2[f] = mytb.getcell('FPARAM',f)
                  else:
                      gain2[f] = mytb.getcell('GAIN',f)
          mytb.close()
          ggx2 = {}
          for g in range(len(gain2)):
# #            print("Appending to ggx: ", gain2[g][0])
              ggx2[g] = gain2[g][0]
          nPolarizations2 = len(gain2[0])
          if (nPolarizations == 2):
              ggy2 = {}
              for g in range(len(gain2)):
                  ggy2[g] = gain2[g][1]
          nRows2 = len(gain2)
          if (debug): print("nRows2 = %s" % (str(nRows2)))

    if (tableFormat == 34):
        # CAS-6801, unfortunately corr_type is not available in the caltable
        mytb.open(caltable)
        spectralWindowTable = mytb.getkeyword('SPECTRAL_WINDOW').split()[1]
        if ('OBSERVATION' in mytb.getkeywords()):
            observationTable = mytb.getkeyword('OBSERVATION').split()[1]
        else:
            observationTable = None
        mytb.open(spectralWindowTable)
        refFreq = mytb.getcol('REF_FREQUENCY')
        net_sideband = mytb.getcol('NET_SIDEBAND')
        measFreqRef = mytb.getcol('MEAS_FREQ_REF')
        mytb.close()
        corr_type = None
        if (os.path.exists(msName)):
          try:
              (corr_type, corr_type_string, nPolarizations) = getCorrType(msName,originalSpwsToPlot,mymsmd,debug)
          except:
              print("4) Could not getCorrType")
        if (corr_type is None):
          if (observationTable is None):
              corr_type, corr_type_string, nPolarizations = getCorrTypeByAntennaName(msAnt[0].lower())
          else:
              telescope = getTelescopeNameFromCaltableObservationTable(observationTable)
              if (telescope.find('ALMA') >= 0):
                  print("Using telescope name (%s) to set the polarization type." % (telescope))
                  corr_type_string = ['XX','YY']
                  corr_type = [9,12]
              elif (telescope.find('VLA') >= 0):
                  print("Using telescope name (%s) to set the polarization type." % (telescope))
                  corr_type_string = ['RR','LL']
                  corr_type = [5,8]
              else:
                  corr_type, corr_type_string, noPolarizations = getCorrTypeByAntennaName(msAnt[0].lower())
    else:
      try:
        if (DEBUG):
            print("Trying to open %s" % (msName+'/SPECTRAL_WINDOW'))
        mytb.open(msName+'/SPECTRAL_WINDOW')
        refFreq = mytb.getcol('REF_FREQUENCY')
        net_sideband = mytb.getcol('NET_SIDEBAND')
        measFreqRef = mytb.getcol('MEAS_FREQ_REF')
        mytb.close()

#        (corr_type, corr_type_string, nPolarizations) = getCorrType(msName, spwsToPlot, mymsmd, debug)
        (corr_type, corr_type_string, nPolarizations) = getCorrType(msName, originalSpwsToPlot, mymsmd, debug)
        if (corr_type_string == []):
            return()
      except:
        print("4) Could not open the associated measurement set tables (%s). Will not translate antenna names." % (msName))
        mymsmd = ''
        print("I will assume ALMA data: XX, YY, and refFreq=first channel.")
#        chanFreqGHz = []  # comment out on 2014-04-08
        corr_type_string = ['XX','YY']
        corr_type = [9,12]

    if (len(polsToPlot) > len(corr_type)):
        # Necessary for SMA (single-pol) data
        polsToPlot = corr_type_string
    casalogPost(debug,"Polarizations to plot = %s" % (str(polsToPlot)))
    polsToPlot = checkPolsToPlot(polsToPlot, corr_type_string, debug)
    if (polsToPlot == []):
        return()

    if (len(msAnt) > 0):
        msFound = True
    else:
        if (xaxis.find('freq')>=0 and tableFormat==33):
            print("Because I could not open the .ms, you cannot use xaxis='freq'.")
            return()
        if (showatm == True or showtsky==True):
            print("Because I could not open the .ms, you cannot use showatm or showtsky.")
            return()

    if (bpoly == False):
        if (debug):
            print("nPolarizations = %s" % (nPolarizations))
            print("nFields = %d = %s" % (nFields, str(uniqueFields)))

    if (bOverlay and debug):
          print("nPolarizations2 = %s" % (str(nPolarizations2)))
          print("nFields2 = %d = %s" % (nFields2, str(uniqueFields2)))
          print("nRows2 = %s" % (str(nRows2)))
    uniqueAntennaIds = np.sort(np.unique(ant))

    yPhaseLabel = 'Phase (deg)'
    tsysPercent = True
    ampPercent = True
    if (VisCal.lower().find('tsys') >= 0):
        if (channeldiff > 0):
            if (tsysPercent):
                yAmplitudeLabel = "Tsys derivative (%_of_median/channel)"
            else:
                yAmplitudeLabel = "Tsys derivative (K/channel)"
        else:
            yAmplitudeLabel = "Tsys (K)"
    else:
        if (yaxis.lower().find('db')>=0):
            yAmplitudeLabel = "Amplitude (dB)"
        else:
            if (channeldiff > 0):
                if (ampPercent):
                    yAmplitudeLabel = "Amp derivative (%_of_median/channel)"
                else:
                    yAmplitudeLabel = "Amplitude derivative"
                yPhaseLabel = 'Phase derivative (deg/channel)'
            else:
                yAmplitudeLabel = "Amplitude"

    madsigma = channeldiff # for option channeldiff>0, sets threshold for finding outliers
    ampMin = LARGE_POSITIVE
    ampMax = LARGE_NEGATIVE
    PHASE_ABS_SUM_THRESHOLD = 2e-3  # in degrees, used to avoid printing MAD statistics for refant

    TDMisSecond = False
    pagectr = 0
    drewAtmosphere = False
    newpage = 1
    if debug:
        print("Setting pages to blank list")
    pages =  []
    xctr = 0
    myap = 0  # determines whether an amp or phase plot starts the page (in the case of 'both')
              # zero means amplitude, 1 means phase
    redisplay = False
    matchctr = 0
    myUniqueColor = []
    # for the overlay=antenna case, start by assuming the first antenna is not flagged
    firstUnflaggedAntennaToPlot = 0
    lastUnflaggedAntennaToPlot = len(antennasToPlot)
    computedAtmSpw = -1
    computedAtmTime = -1
    computedAtmField = -1
    skippingSpwMessageSent = 0
    atmString = ''
    if (showimage and lo1==''):
        # We only need to run this once per execution.
        if (debug):
            print("Calling getLOs")
        getLOsReturnValue = getLOs(msName, verbose=debug)
        if (getLOsReturnValue != []):
            if (debug):
                print("Calling interpret LOs")
            lo1s = interpretLOs(msName,parentms,verbose=debug,mymsmd=mymsmd)
            if (debug):
                print("Done interpretLOs")
            foundLO1Message = []  # Initialize so that message is only displayed once per spw

    if (channeldiff>0):
        # build blank dictionary:  madstats['DV01']['spw']['time']['pol']['amp' or 'phase' or both]
        #                          where spw, time, pol are each integers
        if (len(msAnt) > 0):
            madstats = dict.fromkeys(mymsmd.antennanames(antennasToPlot))
        else:
            madstats = dict.fromkeys(['Ant '+str(i) for i in range(len(uniqueAntennaIds))])

        for i in range(len(madstats)):
            madstats[list(madstats.keys())[i]] = dict.fromkeys(spwsToPlot)
            for j in range(len(spwsToPlot)):
                madstats[list(madstats.keys())[i]][spwsToPlot[j]] = dict.fromkeys(timerangeList) # dict.fromkeys(range(len(uniqueTimes)))
                for k in timerangeList: # range(len(uniqueTimes)):
                    madstats[list(madstats.keys())[i]][spwsToPlot[j]][k] = dict.fromkeys(list(range(nPolarizations)))
                    for l in range(nPolarizations):
                        if (yaxis == 'both'):
                            madstats[list(madstats.keys())[i]][spwsToPlot[j]][k][l] = {'amp': None, 'phase': None}
                        elif (yaxis == 'phase'):
                            madstats[list(madstats.keys())[i]][spwsToPlot[j]][k][l] = {'phase': None}
                        else:
                            # this includes tsys and amp
                            madstats[list(madstats.keys())[i]][spwsToPlot[j]][k][l] = {'amp': None}
        madstats['platforming'] = {}
# #      print("madstats = ", madstats)
    myinput = ''
    atmEverBeenShown = False
    spwsToPlotInBaseband = []
    frequencyRangeToPlotInBaseband = []
    if (debug): print("up to basebands")
    if (len(basebands) == 0):
        # MS is too old to have BBC_NO
        if (debug): print("MS is too old to have BBC_NO")
        spwsToPlotInBaseband = [spwsToPlot]
        frequencyRangeToPlotInBaseband = [callFrequencyRangeForSpws(mymsmd, spwsToPlot, vm, caltable)]
        basebands = [0]
    elif (overlayBasebands):
      if (debug): print("overlayBaseband")
      if (list(spwsToPlot) != list(uniqueSpwsInCalTable)):
          # then spws were requested, so treat them all as if in the same baseband, and
          # ignore the basebands parameter
          print("Ignoring the basebands parameter because spws were specified = %s" % (str(spwsToPlot)))
      elif (np.array_equal(np.sort(basebands), np.sort(allBasebands)) == False):
          # Allow the basebands parameter to select the spws
          if (debug): print("Allow the basebands parameter to select the spws")
          basebandSpwsToPlot = []
          for baseband in basebands:
              myspws = list(getSpwsForBaseband(vis=msName, mymsmd=mymsmd, bb=baseband))
              basebandSpwsToPlot += myspws
          spwsToPlot = np.intersect1d(basebandSpwsToPlot, spwsToPlot)
          print("selected basebands %s have spwsToPlot = %s" % (str(basebands),str(spwsToPlot)))
      spwsToPlotInBaseband = [spwsToPlot]  # treat all spws as if in the same baseband
      frequencyRangeToPlotInBaseband = [callFrequencyRangeForSpws(mymsmd, spwsToPlot, vm, caltable)]
      basebands = [0]
    else:
        print("basebands = %s, spwsToPlot=%s" % (basebands,spwsToPlot))
        for baseband in basebands:
            myspwlist = []
            for spw in spwsToPlot:
                if (ctsys.compare_version('>=',[4,1,0]) and msFound):
                    if (mymsmd.baseband(originalSpwsToPlot[list(spwsToPlot).index(spw)]) == baseband):
                        myspwlist.append(spw)
                else:
                    # need to write a function to retrieve baseband
                    # if (spw != 0):
                    myspwlist.append(spw)
            spwsToPlotInBaseband.append(myspwlist)
            frequencyRangeToPlotInBaseband.append(callFrequencyRangeForSpws(mymsmd, myspwlist,vm,caltable))

    firstTimeMatch = -1    # Aug 5, 2013
    finalTimeMatch = -1 #  for CAS-7820
    groupByBaseband = False # don't activate this parameter yet
    if (overlaySpws or overlayBasebands):
        groupByBaseband = True
    if (groupByBaseband and overlaySpws==False and overlayBasebands==False):
        showBasebandNumber = True
    # Basic nested 'while' loop structure is:
    #   - antennas
    #     - baseband (if necessary)
    #       - spw
    #         - time
    #           - for i in rows
    maxChannels = {};     maxChannels2 = {}
    while (xctr < len(antennasToPlot)):
      if (debug): print("at top of xctr loop: %d" % (xctr))
      xant = antennasToPlot[xctr]
      bbctr = 0
      spwctr = 0
      spwctrFirstToPlot = 0
      antstring, Antstring = buildAntString(xant,msFound,msAnt)
      alreadyPlottedAmp = False  # needed for (overlay='baseband', yaxis='both')  CAS-6477
      finalSpwWasFlagged = False   # inserted on 22-Apr-2014 for g25.27
      if debug:
          print("if (bbctr=%d <? %d and groupByBaseband=%s) or (spwctr=%d < %d and not groupByBaseband=%s)" % (bbctr,len(spwsToPlotInBaseband), groupByBaseband, spwctr, len(spwsToPlot), groupByBaseband))
      while ((bbctr < len(spwsToPlotInBaseband) and groupByBaseband) or
             (spwctr < len(spwsToPlot) and groupByBaseband==False)
             ):
       if (debug): print("at top of bbctr/spwctr loop with bbctr=%d, spwctr=%d" % (bbctr,spwctr))
       if (groupByBaseband):
          baseband = basebands[bbctr]
          spwsToPlot = spwsToPlotInBaseband[bbctr]
          if (debug): print("setting spwsToPlot for baseband %d (bbctr=%d) to %s" % (baseband, bbctr, str(spwsToPlot)))
       else:
           baseband = 0  # add from here to "ispw=" on 2014-04-05
           if (ctsys.compare_version('>=',[4,1,0])):
               if (debug): print("TASK> msName=%s, vis=%s" % (msName,vis))
               if (getBasebandDict(vis=msName,caltable=caltable,mymsmd=mymsmd) != {}):
                   try:
                       baseband = mymsmd.baseband(originalSpwsToPlot[spwctr])
                       if (baseband not in basebands):
                           spwctr += 1
                           if (debug): print("A)incrementing spwctr")
                           continue
                   except:
                       pass
       if (debug):
           if (overlayBasebands):
               print("Regardless of baseband (%s), plotting all spws: %s" % (basebands,str(spwsToPlot)))
           else:
               print("Showing baseband %d containing spws: %s" % (baseband,str(spwsToPlot)))
       if (bbctr < len(spwsToPlotInBaseband)):
           if (debug):
               print("A) spwctr=%d,  bbctr=%d < len(spwsToPlotInBaseband)=%d" % (spwctr,bbctr,len(spwsToPlotInBaseband)))
           spwctr = 0
           spwctrFirstToPlot = spwctr
       firstSpwMatch = -1
       while (spwctr < len(spwsToPlot)):
                if (debug): print("at top of spwctr loop, spwctr=%d" % (spwctr))
                allTimesFlaggedOnThisSpw = True # used only by overlay='time'
                if (groupByBaseband == False):
                    baseband = 0
                    if (ctsys.compare_version('>=',[4,1,0])):
                        if (getBasebandDict(vis=msName,caltable=caltable,mymsmd=mymsmd) != {}):
                            try:
                                baseband = mymsmd.baseband(originalSpwsToPlot[spwctr])
                                if (baseband not in basebands):
                                    #                          print("spw %d=%d: baseband %d is not in %s" % (spwsToPlot[spwctr],originalSpwsToPlot[spwctr], baseband, basebands))
                                    if (debug): print("Bb)incrementing spwctr")
                                    spwctr += 1
                                    continue
                            except:
                                pass
                ispw = spwsToPlot[spwctr]
                ispwInCalTable = list(uniqueSpwsInCalTable).index(ispw)
                mytime = 0
                if (debug):
                    print("+++++++ set mytime=0 for ispw=%d, len(chanFreqGHz) = %d" % (ispw, len(chanFreqGHz)))
                if (overlayAntennas):
                    xctr = -1
                if (overlayTimes):
                    # since the times/scans can vary between spws, redefine nUniqueTimes for each spw
                    nUniqueTimes = len(uniqueTimesCopy)
                    uniqueTimes = uniqueTimesCopy[:]
                    uniqueTimesForSpw = []
                    testtime = 0
                    while (testtime < nUniqueTimes):
                        if (ispw in cal_desc_id[np.where(uniqueTimes[testtime] == times)[0]]):
                            uniqueTimesForSpw.append(uniqueTimes[testtime])
                        testtime += 1
                    uniqueTimes = uniqueTimesForSpw[:]
                    if (tableFormat >= 34):
                        scansForUniqueTimes, nUniqueTimes = computeScansForUniqueTimes(uniqueTimes, cal_scans, times, unique_cal_scans)
                    else:
                        nUniqueTimes = len(uniqueTimes)
#                currentSpwctr = spwctr   # commented out on 2014-04-04 to match task for task01 regression
                if (overlaySpws or overlayBasebands):
                    if (xctr >= firstUnflaggedAntennaToPlot):
                        if (debug):
                            print("xctr=%d >= firstUnflaggedAntennaToPlot=%d, decrementing spwctr to %d" % (xctr, firstUnflaggedAntennaToPlot,spwctr-1))
                        spwctr -= 1

                firstTimeMatch = -1
                finalTimeMatch = -1 #  for CAS-7820
                while (mytime < nUniqueTimes):  # start of enormous 2470-line while loop
                  finalTimerangeFlagged = False  # 04-Aug-2014
                  if (debug):
                      print("at top of mytime loop: mytime = %d < %d" % (mytime,nUniqueTimes))
                      print("%d timerangeListTimes" % (len(timerangeListTimes)))
                      print("timerangeList = %s" % (str(timerangeList)))
                      print("timerangeListTimes = %s" % (str(timerangeListTimes)))
                      print("debugSloppyMatch = %s" % (str(debugSloppyMatch)))
                      print(("solutionTimeThresholdSeconds = %s" % (str(solutionTimeThresholdSeconds))))
#                  if ((scansToPlot == scansToPlotPerSpw[ispw]).all() == False and False):
#                      print "          scansToPlot = ", scansToPlot
#                      print "scansToPlotPerSpw[%2d] = " % (ispw), scansToPlotPerSpw[ispw]
                  if (len(timerangeList) > 0 and
                      (sloppyMatch(uniqueTimes[mytime],timerangeListTimes,solutionTimeThresholdSeconds,
                                   mytime, scansToPlot, scansForUniqueTimes, myprint=debugSloppyMatch)==False)): # task version
#                                    mytime, scansToPlotPerSpw[ispw], scansForUniqueTimes, myprint=debugSloppyMatch)==False)):  # causes infinite loop on test 85
                      if (debug):
                          print("Skipping time %d because it is not in the list: %s" % (mytime, str(timerangeList)))
                      mytime += 1
                      if (debug): print("  0006  incrementing mytime to ", mytime)
                      if (mytime == nUniqueTimes and overlayTimes and overlayAntennas):
                          # added March 14, 2013 to support the case when final timerange is flagged
                          doneOverlayTime = False
                          if (debug):
                              print("$$$$$$$$$$$$$$$$$$$$$$  Setting doneOverlayTime=False" % (xframe))
                      else:
                          if (debug):
                              print("Not setting doneOverlayTime=False because either mytime(%d) != nUniqueTimes(%d) or we are not overlaying time and antenna" % (mytime,nUniqueTimes))
                      continue
                  if (overlayAntennas):
                      xctr += 1
                      if (xctr >= len(antennasToPlot)):
                          xctr = 0
#                          if (mytime == 0):
#                              if (debug): print "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ setting firstTimeMatch = -1"
#                              firstTimeMatch = -1  # Aug 5, 2013
                      xant = antennasToPlot[xctr]
                      if (debug):
                          print("mytime=%d, Set xant to %d" % (mytime,xant))
                      antennaString = ''
                  else:
                      antennaString = 'Ant%2d: %s,  ' % (xant,antstring)
                  if (overlaySpws or overlayBasebands):
                      if (debug): print("C)incrementing spwctr to %d" % (spwctr+1))
                      spwctr += 1
                      if (spwctr >= len(spwsToPlot)):
                          if (debug): print("---------------------- C) Setting spwctr=0")
                          spwctr = 0
                          if (xctr < firstUnflaggedAntennaToPlot):
                              xctr += 1
                              if (xctr == len(antennasToPlot)):
                                  break
                              xant = antennasToPlot[xctr]
                              antstring = buildAntString(xant,msFound,msAnt)
                              if (debug):
                                  print("mytime=%d, Set xant to %d" % (mytime,xant))
                              antennaString = 'Ant%2d: %s,  ' % (xant,antstring)
                          if (overlayBasebands):
                              # Added on 7/29/2014 to fix infinite loop in uid___A002_X652932_X20fb bandpass
                              if (mytime == nUniqueTimes):
                                  spwctr = len(spwsToPlot)
                                  break
                      ispw = spwsToPlot[spwctr]
                      ispwInCalTable = list(uniqueSpwsInCalTable).index(ispw)
                      if (debug):
                          print("----------------------------- spwctr=%d, ispw set to %d, xctr=%d" % (spwctr,ispw,xctr))
                  #  endif overlaySpws or overlayBasebands
                  if (newpage==1):
                      # add the current page (being created here) to the list
                      if (debug):
                          print("pages = ", pages)
                      pages.append([xctr,spwctr,mytime,0])
                      if (debug):
                          print("next: appending [%d,%d,%d,%d]" % (xctr,spwctr,mytime,0))
                      newpage = 0
                  if (ispw not in uniqueSpwsInCalTable):                                   ##### added 2024Aug
                      print("spw %d is not in caltable=%s" % (ispw,uniqueSpwsInCalTable))  ##### added 2024Aug
                      return                                                               ##### added 2024Aug
                  if tableFormat > 33:                                                     ##### added 2024Aug
                      if ispw not in list(scansToPlotPerSpw.keys()):                       ##### added 2024Aug
                          print("ispw=%d not in %s" % (ispw,scansToPlotPerSpw))            ##### added 2024Aug
                          break                                                            ##### added 2024Aug
                  gplotx = []
                  gploty = []
                  channels = []
                  xchannels = []
                  ychannels = []
                  frequencies = []
                  xfrequencies = []
                  yfrequencies = []
                  channels2 = []
                  xchannels2 = []
                  ychannels2 = []
                  frequencies2 = []
                  xfrequencies2 = []
                  yfrequencies2 = []
                  gplotx2 = []
                  gploty2 = []
                  xflag = []
                  yflag = []
                  xflag2 = []
                  yflag2 = []
                  matchFound = False
                  matchField = -1
                  matchRow = -1
                  matchTime = -1
                  if (debug): print("looping over all nRows = %d" % (nRows))
                  for i in range(nRows):
                      if (overlayTimes or overlayAntennas or len(fieldsToPlot)>1 or
                          (nFields>1 and len(fieldlist)<nFields)):
                          # When there are multiple fields, then matching by scan causes the first
                          # matching solution to be displayed every time.  So use the original method
                          # of matching by time until I think of something better.
                          sm = sloppyMatch(uniqueTimes[mytime],times[i],solutionTimeThresholdSeconds,myprint=False)
                      else:
                          if (overlayBasebands):
                              sTP = scansToPlot
                          else:
                              if tableFormat > 33:                         ### added 2024Aug
                                  sTP = scansToPlotPerSpw[ispw]            ### indented 2024Aug
                              else:                                        ### added 2024Aug
                                  sTP = []                                 ### added 2024Aug
                          sm = sloppyMatch(uniqueTimes[mytime],times[i],solutionTimeThresholdSeconds,
                                           mytime, sTP, scansForUniqueTimes, myprint=False) # au version
                      if ((ant[i]==xant) and (cal_desc_id[i]==ispw) and sm
                          and (mytime in timerangeList)   # this test was added to support multiFieldInTimeOverlay
                          ):
                          if (debug): print("len(chanFreqGHz)=%d, ispw=%d" % (len(chanFreqGHz),ispw))
                          if (msFound or tableFormat==34):
                              if (len(chanFreqGHz[ispw]) == 1):
                                  if ((skippingSpwMessageSent & (1<<ispw)) == 0):
                                      casalogPost(debug,"Skipping spw=%d because it has only 1 channel." % (ispw))
                                      skippingSpwMessageSent |= (1<<ispw)
                                  break
                          if (fields[i] in fieldsToPlot):
                              interval = intervals[i] # used for CalcAtmTransmission
                              myFieldIndex = np.where(fields[i] == uniqueFields)[0]
                              if (type(myFieldIndex) == list or type(myFieldIndex) == np.ndarray):
                                  myFieldIndex = myFieldIndex[0]
                              if (debug):
                                  print("%d Found match at field,ant,spw,mytime,time = %d(index=%d),%d,%d,%d,%f=%s" % (matchctr,fields[i],myFieldIndex,xant,ispw,mytime,uniqueTimes[mytime],utstring(uniqueTimes[mytime],4)))
                              if (matchFound):
                                  if (myFieldIndex == matchField and matchTime==times[i]):
                                      print("WARNING: multiple rows for field=%d,ant=%d,spw=%d,scan=%d,time=%d=%.0f=%s,row=%d. Only showing the first one." % (fields[i],xant,ispw,scansForUniqueTimes[mytime],mytime,uniqueTimes[mytime],utstring(uniqueTimes[mytime],3),i))
                              else:
                                  matchFound = True
                                  fieldIndex = myFieldIndex
                                  matchField = myFieldIndex
                                  matchTime = times[i]
                                  matchRow = i
                                  if (msFound or tableFormat==34):
                                      nChannels = len(chanFreqGHz[ispw])
                                  else:
                                      nChannels = len(ggx[0])
                                  xflag.append(flags[i][0][:])
                                  yflag.append(flags[i][1][:])
                                  BRowNumber = i
                                  for j in range(nChannels):   # len(chanFreqGHz[ispw])):
                                      channels.append(j)  # both flagged and unflagged
                                      if (msFound or tableFormat==34):
                                          frequencies.append(chanFreqGHz[ispw][j])
                                          if (j==0 and debug):
                                              print("found match: ispw=%d, j=%d, len(chanFreqGHz)=%d, chanFreqGHz[0]=%f" % (ispw,j, len(chanFreqGHz),chanFreqGHz[ispw][0]))
                                      if (showflagged or (showflagged == False and flags[i][0][j]==0)):
                                          gplotx.append(ggx[i][j])
                                          xchannels.append(j)
                                          if (msFound or tableFormat==34):
                                              xfrequencies.append(chanFreqGHz[ispw][j])
                                      if (nPolarizations == 2):
                                          if (showflagged or (showflagged == False and flags[i][1][j]==0)):
                                              gploty.append(ggy[i][j])
                                              ychannels.append(j)
                                              if (msFound or tableFormat==34):
                                                  yfrequencies.append(chanFreqGHz[ispw][j])
                  # end 'for i' over rows
                  
#                  if (not matchFound and newpage==0 and firstTimeMatch==-1):
                  if (not matchFound and newpage==0):
                    # the first test below is the first of two fixes for CAS-13568 (prevent crash)
                    if (len(pages) > 1) and (subplot==11 or (subplot!=11 and firstSpwMatch==-1 and firstTimeMatch==-1)):
                          # Fix for CAS-7753
                          # the firstTimeMatch part was needed for regression 65: different antennas having different solution times
                          newpage = 1
                          if debug: print("setting pages to length=%d" % (len(pages)-1))
                          pages = pages[:len(pages)-1]
                  myspw = originalSpw[ispw]
                  if myspw not in list(maxChannels.keys()):
                      maxChannels[myspw] = 0  # keep track to set x-axis label correctly
                      maxChannels2[myspw] = 0  # keep track to set x-axis label correctly
           
                  if len(xchannels) > maxChannels[myspw]:
                      maxChannels[myspw] = len(xchannels)  # keep track to set x-axis label correctly
                  if len(xchannels2) > maxChannels[myspw]:
                      maxChannels2[myspw] = len(xchannels2)  # keep track to set x-axis label correctly
                  if (msFound):
                      if debug:
                          print("A) xchannels = ", xchannels)
                          print("myspw=%s" % (str(myspw)))
                          print("len(refFreq)=%d" % (len(refFreq)))
                      if (myspw >= len(refFreq)):
                          myspw = ispw
                  if (msFound and refFreq[myspw]*1e-9 > 60):
                    # Then this cannot be Band 1 or EVLA data.  TODO: But I should really check the telescope name!
#                    if (refFreq[myspw]*1e-9 > np.mean(frequencies)):
                    if (refFreq[myspw]*1e-9 > np.mean(chanFreqGHz[ispw])):  # this is safer (since frequencies might be an empty list)
                        sideband = -1
#                        xlabelString = "%s LSB Frequency (GHz)" % refTypeToString(measFreqRef[myspw])
                        xlabelString = "%s LSB Frequency (GHz) (%d channels)" % (refTypeToString(measFreqRef[myspw]),maxChannels[myspw])  ### 2024Aug
                    else:
                        sideband = +1
#                        xlabelString = "%s USB Frequency (GHz)" % refTypeToString(measFreqRef[myspw])
                        xlabelString = "%s USB Frequency (GHz) (%d channels)" % (refTypeToString(measFreqRef[myspw]),maxChannels[myspw])  ### 2024Aug
                  else:
                      sideband = -1
#                      xlabelString = "Frequency (GHz)"
                      xlabelString = "Frequency (GHz) (%d channels)" % (maxChannels[myspw])  ### 2024Aug
                  if ((len(frequencies)>0) and (chanrange[1] > len(frequencies))):
                      print("Invalid chanrange (%d-%d) for spw%d in caltable1. Valid range = 0-%d" % (chanrange[0],chanrange[1],ispw,len(frequencies)-1))
                      return()
                  pchannels = [xchannels,ychannels]
                  pfrequencies = [xfrequencies,yfrequencies]
                  gplot = [gplotx,gploty]
                  # We only need to compute the atmospheric transmission if:
                  #   * we have been asked to show it,
                  #   * there is a non-trivial number of channels,
                  #   * the current field is the one for which we should calculate it (if times are being overlaied)
                  #       But this will cause no atmcurve to appear if that field is flagged on the first
                  #       antenna; so, I added the atmEverBeenShown flag to deal with this.
                  #   * the previous calculation is not identical to what this one will be
                  #
                  if ((showatm or showtsky) and (len(xchannels)>1 or len(ychannels)>1) and
                      ((uniqueFields[fieldIndex]==showatmfield or
                       (uniqueFields[fieldIndex] in fieldsToPlot and overlayTimes)) or # this insures a plot if first fieldsToPlot is missing
                       overlayTimes==False or atmEverBeenShown==False) and
                      ((overlayTimes==False and computedAtmField!=fieldIndex) or (computedAtmSpw!=ispw) or
                       (overlayTimes==False and computedAtmTime!=mytime))):
                    atmEverBeenShown = True
                    # The following 'if' is used to avoid wasting time since atm is not shown for
                    # overlay='antenna,time'.
                    if (overlayTimes==False or overlayAntennas==False or True):  # support showatm for overlay='antenna,time'
#       #            if (overlayTimes==False or overlayAntennas==False):
# #     # #            print("CAF, CAS, CAT = ", computedAtmField, computedAtmSpw, computedAtmTime)
                      if (type(fieldIndex) == list or type(fieldIndex) == np.ndarray):
                          computedAtmField = fieldIndex[0]
                      else:
                          computedAtmField = fieldIndex
                      computedAtmSpw = ispw
                      computedAtmTime = mytime
                      atmtime = time.time()
                      asdm = ''
# #     # #            print("A) uniqueFields[%d] = " % (fieldIndex), uniqueFields[fieldIndex])
                      uFFI = uniqueFields[fieldIndex]
                      if (type(uFFI) == type(np.ndarray(0))):
                          uFFI = uFFI[0]
                          if (debug): print("converting uFFI from array to %s" % (str(type(uFFI))))
                      (atmfreq,atmchan,transmission,pwvmean,atmairmass,TebbSky,missingCalWVRErrorPrinted) = \
                         CalcAtmTransmission(channels, frequencies, xaxis, pwv,
                                 vm, mymsmd, msName, asdm, xant, uniqueTimes[mytime],
                                 interval, uFFI, refFreq[originalSpw[ispw]],
                                 net_sideband[originalSpw[ispw]], mytime,
                                 missingCalWVRErrorPrinted, caltable,
                                 Trx=Trx, showtsys=showtsys, verbose=DEBUG)
                      if showtsys:
                          TebbSky = Tsys
                      if (showimage):
#                          print("len(lo1s)=%d =  " % (len(lo1s)), lo1s)
                          if (lo1 != ''):
                              # lo1 was specified on the command line
                              LO1 = lo1
                          else:
                            if (getLOsReturnValue == []):
                              if (lo1 == ''):
                                  print("Because you do not have the ASDM_RECEIVER table, if you want the showimage")
                                  print("option to work, then you must specify the LO1 frequency with lo1=.")
# #     # #                        return()
                              LO1 = lo1
                            else:
                              if (lo1s is None or lo1s == {}):
                                  print("Failed to get LO1, disabling showimage.  Alternatively, you can use printLOsFromASDM and supply the lo1 parameter to plotbandpass.")
                                  showimage = False
                                  LO1 = ''
                              else:
                                if (originalSpw[ispw] not in list(lo1s.keys())):
                                    print("There is a problem in reading the LO1 values, cannot showimage for this dataset.")
                                    showimage = False
                                    LO1 = ''
                                else:
                                  LO1 = lo1s[originalSpw[ispw]]*1e-9
                                  if (ispw not in foundLO1Message):
                                      casalogPost(debug,"For spw %d (%d), found LO1 = %.6f GHz" % (ispw,originalSpw[ispw],LO1))
                                      foundLO1Message.append(ispw)
                      if (LO1):
                          frequenciesImage = list(2*LO1 - np.array(frequencies))
                          xfrequenciesImage = list(2*LO1 - np.array(pfrequencies[0]))
                          yfrequenciesImage = list(2*LO1 - np.array(pfrequencies[1]))
                          pfrequenciesImage = [xfrequenciesImage, yfrequenciesImage]
                          if (debug):
                             print("B) uniqueFields[%d] = %s" % (fieldIndex, str(uniqueFields[fieldIndex])))
                          uFFI = uniqueFields[fieldIndex]
                          if (debug):
                             print("type(uFFI) = %s" % (str(type(uFFI))))
                          if (type(uFFI) == list or type(uFFI) == type(np.ndarray(0))):
                             uFFI = uFFI[0]
                          if (debug):
                             print("uFFI = %s" % (str(uFFI)))
                          (atmfreqImage,atmchanImage,transmissionImage,pwvmean,atmairmass,TebbSkyImage,missingCalWVRErrorPrinted) = \
                              CalcAtmTransmission(channels, frequenciesImage, xaxis,
                                                  pwv, vm, mymsmd, msName, asdm, xant, uniqueTimes[mytime],
                                                  interval, uFFI, refFreq[originalSpw[ispw]],
                                                  net_sideband[originalSpw[ispw]], mytime,
                                                  missingCalWVRErrorPrinted, caltable,
                                                  Trx=Trx, showtsys=showtsys, verbose=DEBUG)
                          # difference between the requested Atm freq and actual Atm calcuation CAS-7715. No longer necessary after CAS-10228.
                          #chanDifference = atmfreq[0]-frequencies[0]
                          #chanImageDifference = atmfreqImage[0]-frequenciesImage[-1]
                          #print("signal SB difference = %f, image SB difference = %f" % (chanDifference, chanImageDifference))
                          if showtsys:
                              TebbSkyImage = TsysImage
                          atmfreqImage = list(2*LO1 - np.array(atmfreqImage)) # + 2*chanDifference + chanImageDifference)  # CAS-7715 adds final 2 terms
                          atmchanImage.reverse()

                      if (overlayTimes):
                          atmString = 'PWV %.2fmm, airmass %.2f, maxAlt %.0fkm (field %d)' % (pwvmean,atmairmass,maxAltitude,showatmfield)
                      else:
                          atmString = 'PWV %.2fmm, airmass %.3f, maxAlt %.0fkm' % (pwvmean,atmairmass,maxAltitude)
                  if (bOverlay):
                    for i in range(nRows2):
                      if (overlayTimes or overlayAntennas or len(fieldsToPlot)>1 or
                          (nFields>1 and len(fieldlist)<nFields)):
                          # Not having this path causes Tsys table overlays to behave like overlay='antenna,time'
                          # for caltable2.
                          sm = sloppyMatch(uniqueTimes2[mytime],times2[i],solutionTimeThresholdSeconds,myprint=False)
                      else:
                          if (mytime >= len(uniqueTimes2)):
                              # Fix for CAS-9474: avoid calling sloppyMatch because it will crash.
                              # Setting sm=False will result in an abort: "no amp data found in second solution."
                              sm = False
                          else:
                              if tableFormat >= 34:                            ### added 2024Aug
                                  scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
                              else:                                            ### added 2024Aug
                                  scansToPlotHere = scansToPlot                ### added 2024Aug
                              sm = sloppyMatch(uniqueTimes2[mytime],times2[i],solutionTimeThresholdSeconds,
                                           mytime, scansToPlotHere, scansForUniqueTimes,  # au version     ### modified 2024Aug
                                           myprint=debugSloppyMatch)
                      if ((ant2[i]==xant) and (cal_desc_id2[i]==ispw) and sm
                          and (mytime in timerangeList)   # added to match first caltable logic on 2014-04-09
                          ):
                          if (fields2[i] in fieldsToPlot):
                                xflag2.append(flags2[i][0][:])
                                yflag2.append(flags2[i][1][:])
                                # With solint='2ch' or more, the following loop should not be over
                                # chanFreqGHz2 but over the channels in the solution.
                                for j in range(len(chanFreqGHz2[ispw])):
                                  channels2.append(j)
                                  frequencies2.append(chanFreqGHz2[ispw][j])
# #     # #                        print("len(chanFreqGHz2[%d])=%d, i=%d,j=%d, len(ggx2)=%d, len(ggx2[0])=%d, shape(ggx2) = " % (ispw,len(chanFreqGHz2[ispw]),i,j,len(ggx2),len(ggx2[0])), np.shape(np.array(ggx2)))
                                  if (showflagged or (showflagged == False and flags2[i][0][j]==0)):
                                      gplotx2.append(ggx2[i][j])
                                      xchannels2.append(j)
                                      xfrequencies2.append(chanFreqGHz2[ispw][j])
                                  if (nPolarizations2 == 2):
                                      if (showflagged or (showflagged == False and flags2[i][1][j]==0)):
                                          gploty2.append(ggy2[i][j])
                                          ychannels2.append(j)
                                          yfrequencies2.append(chanFreqGHz2[ispw][j])
                    # end 'for i'
                    pchannels2 = [xchannels2,ychannels2]
                    pfrequencies2 = [xfrequencies2,yfrequencies2]
                    gplot2 = [gplotx2,gploty2]
                    # Need to rewrite the xlabel to show the total channel numbers from both caltables.  Note that xaxis must be 'freq' for bOverlay
                    if (msFound and refFreq[myspw]*1e-9 > 60): 
                        # Then this cannot be Band 1 or EVLA data.  But I should really check the telescope name!
                        if (refFreq[myspw]*1e-9 > np.mean(chanFreqGHz2[ispw])):  # this is safer (since frequencies might be [])
                            sideband = -1
                            xlabelString = "%s LSB Frequency (GHz) (%d, %d channels)" % (refTypeToString(measFreqRef[myspw]),maxChannels[myspw],maxChannels2[myspw])
                        else:
                            sideband = +1
                            xlabelString = "%s USB Frequency (GHz) (%d, %d channels)" % (refTypeToString(measFreqRef[myspw]),maxChannels[myspw],maxChannels2[myspw])
                    else:
                        sideband = -1
                        xlabelString = "Frequency (GHz) (%d, %d channels)" % (maxChannels[myspw],maxChannels2[myspw])
                  # endif bOverlay

                  if (matchFound==False):
                      if ((overlayAntennas==False and overlaySpws==False and overlayBasebands==False) or
                          (overlayAntennas and xctr+1 >= len(antennasToPlot)) or
                          ((overlaySpws or overlayBasebands) and spwctr+1 >= len(spwsToPlot))):
                          mytime += 1
                          if (debug):
                              print("a) xctr=%d, Incrementing mytime to %d" % (xctr, mytime))
                      if overlayAntennas and xctr+1 == len(antennasToPlot): # second fix for CAS-13568
                          DrawAntennaNamesForOverlayAntennas(xstartPolLabel, ystartPolLabel, polsToPlot, corr_type, channeldiff, ystartMadLabel, subplotRows, gamp_mad, gamp_std, overlayColors, mysize, ampmarkstyle, markersize, markeredgewidth, msAnt, msFound, antennasToPlot, ampmarkstyle2, xframe, firstFrame, caltableTitle, titlesize, debug=debug)
                          if ((showatm or showtsky) and len(atmString) > 0):
                              DrawAtmosphere(showatm, showtsky, subplotRows, atmString,
                                             mysize, TebbSky, plotrange, xaxis, atmchan,
                                             atmfreq, transmission, subplotCols,
                                             showatmPoints=showatmPoints, xframe=xframe,
                                             channels=channels,mylineno=lineNumber(),
                                             overlaySpws=overlaySpws,
                                             overlayBasebands=overlayBasebands,
                                             drewAtmosphere=drewAtmosphere,loc=203,
                                             showtsys=showtsys, Trx=Trx)
                              if (LO1):
                                  # Now draw the image band
                                  DrawAtmosphere(showatm,showtsky, subplotRows, atmString,
                                                 mysize, TebbSkyImage, plotrange, xaxis,
                                                 atmchanImage, atmfreqImage, transmissionImage,
                                                 subplotCols, LO1, xframe, firstFrame, showatmPoints,
                                                 channels=channels,mylineno=lineNumber(),
                                                 overlaySpws=overlaySpws,
                                                 overlayBasebands=overlayBasebands,
                                                 drewAtmosphere=drewAtmosphere,loc=204,
                                                 showtsys=showtsys, Trx=Trx)
                              drewAtmosphere = True

                      continue
                  #  The following variable allows color legend of UT times to match line plot
                  myUniqueTime = []
                  if (True):  # multiFieldsWithOverlayTime):
                      # support multi-fields with overlay='time'
                      uTPFPS = []
                      for f in fieldIndicesToPlot:
                          for t in uniqueTimesPerFieldPerSpw[ispwInCalTable][f]:
                              if tableFormat >= 34:                            ### added 2024Aug
                                  scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
                              else:                                            ### added 2024Aug
                                  scansToPlotHere = scansToPlot                ### added 2024Aug
                              if (sloppyMatch(t, timerangeListTimes, solutionTimeThresholdSeconds,
                                              mytime, scansToPlotHere, scansForUniqueTimes, # au version     ### modified 2024Aug
                                              myprint=debugSloppyMatch
                                              )):
                                  uTPFPS.append(t)
                      uTPFPS = np.sort(uTPFPS)
                      ctr = 0
                      for t in uTPFPS:
                          if (debug and False):
                              print("1)checking time %d" % (t))
                          if (overlayTimes or overlayAntennas):
                              sm = sloppyMatch(uniqueTimes[mytime],times[i],solutionTimeThresholdSeconds,myprint=False)
                          else:
                              if tableFormat >= 34:                            ### added 2024Aug
                                  scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
                              else:                                            ### added 2024Aug
                                  scansToPlotHere = scansToPlot                ### added 2024Aug
                              sm = sloppyMatch(t, uniqueTimes[mytime], solutionTimeThresholdSeconds,
                                               mytime, scansToPlotHere, scansForUniqueTimes,  # au version      ### modified 2024Aug
                                               myprint=debugSloppyMatch
                                               )
                          if (sm):
                              if (debug):
                                  print("1)setting myUniqueTime to %d" % (mytime))
                              myUniqueTime = mytime
                              ctr += 1
                      if (ctr > len(fieldIndicesToPlot) and bOverlay==False):
                          if (debug): print("multi-field time overlay ***************  why are there 2 matches?")
# #     # #            if (ctr == 0):
# #     # #                print("No match for %.1f in "%(t), uTPFPS)

# #     # #        print("Overlay antenna %d, myUniqueTime=%d" % (xctr, myUniqueTime))
                  if (xframe == xframeStart):
                        safe_pb_clf()
                  xflag = [item for sublist in xflag for item in sublist]
                  yflag = [item for sublist in yflag for item in sublist]
#       #          pflag = [xflag, yflag]
#       #          flagfrequencies = [frequencies, frequencies2]
                  antstring, Antstring = buildAntString(xant,msFound,msAnt)
                  if (msFound):
                      fieldString = msFields[uniqueFields[fieldIndex]]
                  else:
                      fieldString = str(field)
                  if (overlayTimes):
                      timeString =''
                  else:
                      timeString = ',  t%d/%d  %s' % (mytime,nUniqueTimes-1,utstring(uniqueTimes[mytime],3))
                      if (scansForUniqueTimes != []):
                          if (scansForUniqueTimes[mytime]>=0):
                              timeString = ',  scan%d  %s' % (scansForUniqueTimes[mytime],utstring(uniqueTimes[mytime],3))
                  spwString = buildSpwString(overlaySpws, overlayBasebands, spwsToPlot,
                                             ispw, originalSpw[ispw], observatoryName,
                                             baseband, showBasebandNumber)
                  titleString = "%sspw%s,  field %d: %s%s" % (antennaString,spwString,uniqueFields[fieldIndex],fieldString,timeString)
                  if (sum(xflag)==nChannels and sum(yflag)==nChannels and showflagged==False):
                      if (overlayTimes):
                          msg = "Skip %s (%s) for time%d=%s all data flagged" % (antstring, titleString,mytime,utstring(uniqueTimes[mytime],3))
                          casalogPost(True, msg)
                          # need to set doneOverlayTime = True if this is the final time,
                          # otherwise, we get "subplot number exceeds total subplots" at line 2427
                          # but we need to draw the labels at the top of the page, else they will not get done
                          if (debug):
                              print("########## uniqueTimes[%d]=%d,  timerangeListTimes[-1]=%d" % (mytime,uniqueTimes[mytime],timerangeListTimes[-1]))
                          if (len(scansToPlotPerSpw[ispw]) < 1):
                              sTPPS = []
                          else:
#                              sTPPS = [scansToPlot[-1]]# added [[-1]] on 2014-04-04 for CAS-6394  task version
                              sTPPS = [scansToPlotPerSpw[ispw][-1]]# added [[-1]] on 2014-04-04 for CAS-6394  au version
                          if (sloppyMatch(timerangeListTimes[-1], uniqueTimes[mytime],
                                          solutionTimeThresholdSeconds,
                                          mytime, sTPPS, scansForUniqueTimes,
                                          myprint=debugSloppyMatch
                                          )):
                              if (overlayAntennas == False or xant==antennasToPlot[-1]):  # 11-Mar-2014
                                  doneOverlayTime = True  # 08-Nov-2012
                                  finalTimerangeFlagged =  True  # 04-Aug-2014
                              if (debug):
                                  print("###### set doneOverlayTime = %s" % (str(doneOverlayTime)))

                              # draw labels
                              # try adding the following 'if' statement on Jun 18, 2013; it works.
#                              if (drewAtmosphere==False or overlayAntennas==False):
                              # Add the 'and not' case to prevent extra atm/fdms shown if one spw's solutions are all flagged
                              if (drewAtmosphere==False or (not overlayAntennas and not allTimesFlaggedOnThisSpw)):
                                  if (debug): print("drawOverlayTimeLegends loc 1")
                                  drawOverlayTimeLegends(xframe, firstFrame, xstartTitle, ystartTitle,
                                                         caltable, titlesize, fieldIndicesToPlot,
                                                         ispwInCalTable, uniqueTimesPerFieldPerSpw,
                                                         timerangeListTimes,
                                                         solutionTimeThresholdSeconds,
                                                         debugSloppyMatch,
                                                         ystartOverlayLegend, debug, mysize,
                                                         fieldsToPlot, myUniqueColor,
                                                         timeHorizontalSpacing, fieldIndex,
                                                         overlayColors,
                                                         antennaVerticalSpacing, overlayAntennas,
                                                         timerangeList, caltableTitle, mytime,
                                                         scansToPlotPerSpw[ispw], scansForUniqueTimes,
                                                         uniqueSpwsInCalTable, uniqueTimes)
                                  if not LO1 and type(lo1s) == dict:  # Fix for SCOPS-4877
                                      LO1 = lo1s[myspw]                   # Fix for SCOPS-4877
                                  # CAS-8655
                                  newylimits = drawAtmosphereAndFDM(showatm,showtsky,atmString,subplotRows,mysize,
                                                       TebbSky,TebbSkyImage,plotrange, xaxis,atmchan,
                                                       atmfreq,transmission,subplotCols,showatmPoints,
                                                       xframe, channels,LO1,atmchanImage,atmfreqImage,
                                                       transmissionImage, firstFrame,showfdm,nChannels,
                                                       tableFormat,originalSpw_casa33,
                                                       chanFreqGHz_casa33,originalSpw,chanFreqGHz,
                                                       overlayTimes, overlayAntennas, xant,
                                                       antennasToPlot, overlaySpws, baseband,
                                                       showBasebandNumber, basebandDict,
                                                       overlayBasebands, overlayColors, drewAtmosphere, showtsys)
                                  drewAtmosphere = True
                              if (xctr == firstUnflaggedAntennaToPlot or overlayAntennas==False): # changed xant->xctr on 11-mar-2014
                                  DrawPolarizationLabelsForOverlayTime(xstartPolLabel,ystartPolLabel,corr_type,polsToPlot,
                                                                       channeldiff,ystartMadLabel,subplotRows,gamp_mad,mysize,
                                                                       ampmarkstyle,markersize,ampmarkstyle2,gamp_std)
                      else:  # not overlaying times
                          msg = "Skip %s spw%d (%s) all data flagged" % (antstring, ispw, titleString)
                          casalogPost(True, msg)
                          if ((overlaySpws or overlayBasebands) and spwctr==spwctrFirstToPlot):
                              spwctrFirstToPlot += 1
                          if ((overlaySpws or overlayBasebands) and ispw==spwsToPlotInBaseband[bbctr][-1]):
                              if (debug): print("The final spw was flagged!!!!!!!!!!!!!!")
                              finalSpwWasFlagged =  True  # inserted on 22-Apr-2014 for g25.27
                          if (myinput == 'b'):
                              redisplay = False # This prevents infinite loop when htting 'b' on first screen when ant0 flagged. 2013-03-08
                      if (overlayAntennas==False and overlayBasebands==False): # 07/30/2014  added  overlayBasebands==False
                        if (doneOverlayTime==False or overlayTimes==False):  # added on 08-Nov-2012
                            finalSpwWasFlagged = False # Added on 23-Apr-2014 for regression61
                            mytime += 1
                            if (debug):
                                print("F) all solutions flagged --> incrementing mytime to %d" % mytime)
                      if (overlayAntennas):
                          if (xctr == firstUnflaggedAntennaToPlot):
                              firstUnflaggedAntennaToPlot += 1
                              if (firstUnflaggedAntennaToPlot >= len(antennasToPlot)):
                                  firstUnflaggedAntennaToPlot = 0
                                  if not finalSpwWasFlagged: # Added on 23-Apr-2014 for regression61
                                      mytime += 1
                              if (debug):
                                  print("----- Resetting firstUnflaggedAntennaToPlot from %d to %d = %d" % (firstUnflaggedAntennaToPlot-1, firstUnflaggedAntennaToPlot, antennasToPlot[firstUnflaggedAntennaToPlot]))
                              if (mytime < nUniqueTimes):  # Add this 'if' conditional on 9-22-2015 for CAS-7839
                                  continue  # Try adding this statement on Apr 2, 2012 to fix bug.
                              mytime -= 1  # Add this on 9-22-2015 for CAS-7839
                      if (overlaySpws or overlayBasebands):
                          if (xctr == firstUnflaggedAntennaToPlot):
                              firstUnflaggedAntennaToPlot += 1
                              if (firstUnflaggedAntennaToPlot >= len(antennasToPlot)):
                                  firstUnflaggedAntennaToPlot = 0
                                  if not finalSpwWasFlagged: # Added on 22-Apr-2014 for g25.27 dataset antenna='4'
                                      if (overlayBasebands == False or spwctr>len(spwsToPlot)):  # Added on 7/30/2014 for regression 96
                                          mytime += 1
                              if (debug):
                                  print("----- Resetting firstUnflaggedAntennaToPlot from %d to %d" % (firstUnflaggedAntennaToPlot-1, firstUnflaggedAntennaToPlot))
                                  print("-----    = antenna %d" % (antennasToPlot[firstUnflaggedAntennaToPlot]))
                              if (not finalSpwWasFlagged): # add this test on Apr 22, 2014 to prevent crash on g25.27 dataset with antenna='4,5'
                                  continue # Try this 'continue' on Apr 2, 2012 to fix bug -- works.
                      if (overlayAntennas==False and subplot==11
                          and not finalSpwWasFlagged      # inserted on 22-Apr-2014 for g25.27
                          and not finalTimerangeFlagged): # inserted on 04-Aug-2014 for CAS-6812
                            # added the case (subplot==11) on April 22, 2012 to prevent crash on multi-antenna subplot=421
                            if (debug):
                                print("#######  removing [%d,%d,%d,%d]" % (pages[len(pages)-1][PAGE_ANT],
                                                                  pages[len(pages)-1][PAGE_SPW],
                                                                  pages[len(pages)-1][PAGE_TIME],
                                                                  pages[len(pages)-1][PAGE_AP]))
                            pages = pages[0:len(pages)-1]
                            newpage = 1
                      if (overlayAntennas==False):
                          if (doneOverlayTime==False  # inserted on 08-Nov-2012
                              and not finalSpwWasFlagged):  # inserted on 22-Apr-2014 for g25.27
                              continue
                          elif (debug):
                              print("=========== Not continuing because doneOverlayTime=%s" % (str(doneOverlayTime)))
                  else:
                      allTimesFlaggedOnThisSpw = False
                      if (debug):
                          print("Not all the data are flagged.  doneOverlayTime=%s" % (str(doneOverlayTime)))

                  if (firstSpwMatch == -1):
                      firstSpwMatch = spwctr
                  if (firstTimeMatch == -1):
                      firstTimeMatch = mytime
                      if (debug):
                          print("Setting firstTimeMatch from -1 to %s" % (str(firstTimeMatch)))
                  # The following was needed to support overlay='antenna,time' for showatm for QA2 report (CAS-7820)
                  if (finalTimeMatch == -1 or finalTimeMatch < mytime):
                      if (debug):
                          print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Setting finalTimeMatch from %d to %d" % (finalTimeMatch, mytime))
                      finalTimeMatch = mytime

################### Here is the amplitude plotting ############  
                  if (yaxis.find('amp')>=0 or yaxis.find('both')>=0 or yaxis.find('ap')>=0) and doneOverlayTime==False:
                    if (overlayBasebands and amplitudeWithPhase): # CAS-6477
                        if (float(xframe/10) != xframe*0.1 and alreadyPlottedAmp):
                            xframe -= 2

                    if (debug):
                        print("amp: xctr=%d, xant=%d, myap=%d, mytime=%d(%s), firstTimeMatch=%d, bOverlay=" % (xctr, xant, myap, mytime, utstring(uniqueTimes[mytime],3), firstTimeMatch), bOverlay)
                    if (myap==1):
                      if (overlayTimes == False or mytime==firstTimeMatch):
                        if ((overlaySpws == False and overlayBasebands==False) or spwctr==spwctrFirstToPlot or spwctr>len(spwsToPlot)):
                          if (overlayAntennas==False or xctr==firstUnflaggedAntennaToPlot
                              or xctr==antennasToPlot[-1]):  # 2012-05-24, to fix the case where all ants flagged on one timerange
                              xframe += 1
                              if (debug):
                                  print("y) incrementing xframe to %d" % xframe)
                                  print("mytime=%d  ==  firstTimeMatch=%d" % (mytime, firstTimeMatch))
                                  print("xctr=%d  ==  firstUnflaggedAntennaToPlot=%d,  antennastoPlot[-1]=%d" % (xctr, firstUnflaggedAntennaToPlot,antennasToPlot[-1]))
                              myUniqueColor = []
                              newylimits = [LARGE_POSITIVE, LARGE_NEGATIVE]
                    else: # (myap == 0)
                      if (overlayTimes == False or mytime==firstTimeMatch):
                        if ((overlaySpws == False and overlayBasebands==False) or
                            spwctr==spwctrFirstToPlot or
                            (overlayBasebands and amplitudeWithPhase) or # CAS-6477
                            spwctr>len(spwsToPlot)):
                          if (overlayAntennas==False or xctr==firstUnflaggedAntennaToPlot
                              or xctr>antennasToPlot[-1]):  # 2012-05-24, to fix the case where all ants flagged on one timerange
                              xframe += 1
                              if (debug):
                                  print("Y) incrementing xframe to %d" % xframe)
                                  print("mytime=%d  ==  firstTimeMatch=%d" % (mytime, firstTimeMatch))
                                  print("xctr=%d  ==  firstUnflaggedAntennaToPlot=%d,  antennasToPlot[-1]=%d" % (xctr, firstUnflaggedAntennaToPlot,antennasToPlot[-1]))
                                  print("spwctr=%d  >? len(spwsToPlot)=%d" % (spwctr, len(spwsToPlot)))
                              myUniqueColor = []
                              newylimits = [LARGE_POSITIVE, LARGE_NEGATIVE]
                              if (debug):
                                  print("myap=%d, mytime == firstTimeMatch=%d" % (myap, firstTimeMatch))
                          else:
                              if (debug): print("4)Not incrementing xframe from %d" % (xframe))
                        else:
                            if (debug): print("2)Not incrementing xframe from %d (spwctr=%d >? len(spwsToPlot)=%d) or (spwctr=%d == spwctrFirstToPlot=%d)" % (xframe,spwctr,len(spwsToPlot),spwctr,spwctrFirstToPlot))
                      else:
                          if (debug): print("1)Not incrementing xframe from %d" % (xframe))
                      if (debug):
                          print("$$$$$$$$$$$$$$$$$$$$$$$  ready to plot amp on xframe %d" % (xframe))
# #     # #            print(",,,,,,,,,,,,,,,, Starting with newylimits = ", newylimits)
                      if (previousSubplot != xframe):
                          adesc = safe_pb_subplot(xframe)  # avoid deprecation warning in CASA6 when xframe already was opened
                          drewAtmosphere = False
                      previousSubplot = xframe
                      alreadyPlottedAmp = True  # needed for (overlay='baseband', yaxis='both')  CAS-6477
                      if (delay):
                          gampx = gplotx
                      else:
                          gampx = np.abs(gplotx)
                      if (nPolarizations == 2):
                          if (delay):
                              gampy = gploty
                          else:
                              gampy = np.abs(gploty)
                          if (yaxis.lower().find('db') >= 0):
                              gamp = [10*np.log10(gampx), 10*np.log10(gampy)]
                          else:
                              if (channeldiff>0):
                                  if (debug): print("Computing derivatives")
                                  if (xaxis == 'chan'):
                                      gamp0, newx0, gamp0res, newx0res = channelDifferences(gampx, pchannels[0], resample)
                                      gamp1, newx1, gamp1res, newx1res = channelDifferences(gampy, pchannels[1], resample)
                                      pchannels = [newx0, newx1]
                                  else:
                                      gamp0, newx0, gamp0res, newx0res  = channelDifferences(gampx, pfrequencies[0], resample)
                                      gamp1, newx1, gamp1res, newx1res = channelDifferences(gampy, pfrequencies[1], resample)
                                      pfrequencies = [newx0, newx1]
                                  gamp = [gamp0, gamp1]
                                  gampres = [gamp0res, gamp1res]
                                  if (VisCal.lower().find('tsys') >= 0 and tsysPercent):
                                      gamp = [100*gamp0/np.median(gampx), 100*gamp1/np.median(gampy)]
                                      gampres = [100*gamp0res/np.median(gampx), 100*gamp1res/np.median(gampy)]
                                  elif (VisCal.lower().find('tsys') < 0 and ampPercent):
                                      gamp = [100*gamp0/np.median(gampx), 100*gamp1/np.median(gampy)]
                                      gampres = [100*gamp0res/np.median(gampx), 100*gamp1res/np.median(gampy)]
                                  gamp_mad = [madInfo(gamp[0],madsigma,edge), madInfo(gamp[1],madsigma,edge)]
                                  gamp_std = [stdInfo(gampres[0],madsigma,edge,ispw,xant,0), stdInfo(gampres[1],madsigma,edge,ispw,xant,1)]
                                  if (debug): print("gamp_mad done")
                                  if (platformingSigma > 0):
                                      platformingThresholdX = gamp_mad[0]['mad']*platformingSigma
                                      platformingThresholdY = gamp_mad[1]['mad']*platformingSigma
                                  else:
                                      platformingThresholdX = platformingThreshold
                                      platformingThresholdY = platformingThreshold
                                  gamp_platforming = [platformingCheck(gamp[0],platformingThresholdX),
                                                      platformingCheck(gamp[1],platformingThresholdY)]
                                  for p in [0,1]:
                                      if (debug):
                                          print("gamp_mad[%d] = %s" % (p, str(gamp_mad[p])))
                                          print("madstats[%s][%d] = %s" % (Antstring,ispw, str(madstats[Antstring][ispw])))
                                      madstats[Antstring][ispw][mytime][p]['amp'] = gamp_mad[p]['mad']
                                      madstats[Antstring][ispw][mytime][p]['ampstd'] = gamp_std[p]['std']
                                      if (gamp_platforming[p]):
                                          if (Antstring not in list(madstats['platforming'].keys())):
                                              madstats['platforming'][Antstring] = {}
                                          if (ispw not in list(madstats['platforming'][Antstring].keys())):
                                              madstats['platforming'][Antstring][ispw] = {}
                                          if (p not in list(madstats['platforming'][Antstring][ispw].keys())):
                                              madstats['platforming'][Antstring][ispw][p] = []
                                          madstats['platforming'][Antstring][ispw][p].append(uniqueTimes[mytime])
                                      if (gamp_mad[p]['nchan'] > 0):
                                          casalogPost(debug, "%s, Pol %d, spw %2d, %s, amp: %4d points exceed %.1f sigma (worst=%.2f at chan %d)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), gamp_mad[p]['nchan'], madsigma, gamp_mad[p]['outlierValue'], gamp_mad[p]['outlierChannel']+pchannels[p][0]))
                                  if (debug): print("madstats done")
                              else:
                                  gamp = [gampx,gampy]
                      else:
                          if (yaxis.lower().find('db') >= 0):
                              gamp = [10*np.log10(gampx)]
                          else:
                              if (channeldiff>0):
                                  if (xaxis == 'chan'):
                                      gamp0, newx0, gamp0res, newx0res  = channelDifferences(gampx, pchannels[0], resample)
                                      pchannels = [newx0]
                                  else:
                                      gamp0, newx0, gamp0res, newx0res  = channelDifferences(gampx, pfrequencies[0], resample)
                                      pfrequencies = [newx0]
                                  gamp = [gamp0]
                                  gampres = [gamp0res]
                                  if (VisCal.lower().find('tsys') >= 0 and tsysPercent):
                                      gamp = [100*gamp0/np.median(gampx)]
                                      gampres = [100*gamp0res/np.median(gampx)]
                                  elif (VisCal.lower().find('tsys') < 0 and ampPercent):
                                      gamp = [100*gamp0/np.median(gampx)]
                                      gampres = [100*gamp0res/np.median(gampx)]
                                  p = 0
                                  gamp_mad = [madInfo(gamp[p], madsigma,edge)]
                                  gamp_std = [stdInfo(gampres[p], madsigma,edge,ispw,xant,p)]
                                  if (platformingSigma > 0):
                                      platformingThresholdX = gamp_mad[0]['mad']*platformingSigma
                                  else:
                                      platformingThresholdX = platformingThreshold
                                  gamp_platforming = [platformingCheck(gamp[p], platformingThresholdX)]
                                  madstats[Antstring][ispw][mytime][p]['amp'] = gamp_mad[p]['mad']
                                  madstats[Antstring][ispw][mytime][p]['ampstd'] = gamp_std[p]['std']
                                  if (gamp_platforming[p]):
                                      if (Antstring not in list(madstats['platforming'].keys())):
                                          madstats['platforming'][Antstring] = {}
                                      if (ispw not in list(madstats['platforming'][Antstring].keys())):
                                          madstats['platforming'][Antstring][ispw] = {}
                                      if (p not in list(madstats['platforming'][Antstring][ispw].keys())):
                                          madstats['platforming'][Antstring][ispw][p] = []
                                      madstats['platforming'][Antstring][ispw][p].append(mytime)
                                  if (gamp_mad[p]['nchan'] > 0):
                                      casalogPost(debug, "%s, Pol %d, spw %2d, %s, amp: %4d points exceed %.1f sigma (worst=%.2f at chan %d)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), gamp_mad[p]['nchan'], madsigma, gamp_mad[p]['outlierValue'], gamp_mad[p]['outlierChannel']+pchannels[p][0]))
                              else:
                                  gamp = [gampx]
                      if (bOverlay):
                            gampx2 = np.abs(gplotx2)
                            if (nPolarizations2 == 2):
                              gampy2 = np.abs(gploty2)
                              if (yaxis.lower().find('db') >= 0):
                                  gamp2 = [10*np.log10(gampx2), 10*np.log10(gampy2)]
                              else:
                                  if (channeldiff>0):
                                      if (xaxis == 'chan'):
                                          gamp2_0, newx0, gamp2_0res, newx0res = channelDifferences(gampx2, pchannels2[0], resample)
                                          gamp2_1, newx1, gamp2_1res, newx1res = channelDifferences(gampy2, pchannels2[1], resample)
                                          pchannels2 = [newx0, newx1]
                                      else:
                                          gamp2_0, newx0, gamp2_0res, newx0res = channelDifferences(gampx2, pfrequencies2[0], resample)
                                          gamp2_1, newx1, gamp2_1res, newx1res = channelDifferences(gampy2, pfrequencies2[1], resample)
                                          pfrequencies2 = [newx0, newx1]
                                      gamp2 = [gamp2_0, gamp2_1]
                                      gamp2res = [gamp2_0res, gamp2_1res]
                                      if (VisCal.lower().find('tsys') >= 0 and tsysPercent):
                                          gamp2 = [100*gamp2_0/np.median(gampx2), 100*gamp2_1/np.median(gampy2)]
                                          gamp2res = [100*gamp2_0res/np.median(gampx2), 100*gamp2_1res/np.median(gampy2)]
                                      elif (VisCal.lower().find('tsys') < 0 and ampPercent):
                                          gamp2 = [100*gamp2_0/np.median(gampx2), 100*gamp2_1/np.median(gampy2)]
                                          gamp2res = [100*gamp2_0res/np.median(gampx2), 100*gamp2_1res/np.median(gampy2)]
                                  else:
                                      gamp2 = [gampx2, gampy2]
                            else:
                              if (yaxis.lower().find('db') >= 0):
                                  gamp2 = [10*np.log10(gampx2)]
                              else:
                                  if (channeldiff>0):
                                      if (xaxis == 'chan'):
                                          gamp2_0, newx0, gamp2_0res, newx0res = channelDifferences(gampx2, pchannels[0], resample)
                                          pchannels2 = [newx0]
                                      else:
                                          gamp2_0, newx0, gamp2_0res, newx0res = channelDifferences(gampx2, pfrequencies[0], resample)
                                          pfrequencies2 = [newx0]
                                      gamp2 = [gamp2_0]
                                      gamp2res = [gamp2_0res]
                                      if (VisCal.lower().find('tsys') >= 0 and tsysPercent):
                                          gamp2 = [100*gamp2_0/np.median(gampx2)]
                                          gamp2res = [100*gamp2_0res/np.median(gampx2)]
                                      elif (VisCal.lower().find('tsys') < 0 and ampPercent):
                                          gamp2 = [100*gamp2_0/np.median(gampx2)]
                                          gamp2res = [100*gamp2_0res/np.median(gampx2)]
                                  else:
                                      gamp2 = [gampx2]
                      if (xaxis.find('chan')>=0 or (msFound==False and tableFormat==33)):    #  'amp'
                          if (debug):
                              print("amp: plot vs. channel **********************")
                          for p in range(nPolarizations):
                              if (overlayAntennas or overlayTimes):
                                  if (corr_type_string[p] in polsToPlot):
                                        pdesc = pb.plot(pchannels[p],gamp[p],'%s'%ampmarkstyles[p],
                                                        markersize=markersize,
                                                        markerfacecolor=overlayColors[xctr],markeredgewidth=markeredgewidth)
                                        newylimits =  recalcYlimits(plotrange,newylimits,gamp[p])
                                        if (overlayAntennas and overlayTimes==False):
                                            pb.setp(pdesc, color=overlayColors[xctr])
                                        elif (overlayTimes and overlayAntennas==False):
                                            pb.setp(pdesc, color=overlayColors[mytime])
                                        elif (overlayTimes and overlayAntennas): # try to support time,antenna
                                            if (debug):
                                                print("p=%d, len(fieldsToPlot)=%d, len(timerangeList)=%d" % (p,len(fieldsToPlot),len(timerangeList)))
                                            if (len(fieldsToPlot) > 1 or len(timerangeList)>1):
                                                # The third 'or' below is needed if pol='0' is flagged on antenna 0. -- 2012/10/12
                                                if (p==0 or len(polsToPlot)==1 or myUniqueColor==[]):
                                                    myUniqueColor.append(overlayColors[len(myUniqueColor)])
                                                pb.setp(pdesc, color=myUniqueColor[-1])
                              else:
                                  if (corr_type_string[p] in polsToPlot):
# #     # #                          print("pcolor[%d]=%s" % (p,pcolor))
                                    pb.plot(pchannels[p],gamp[p],'%s%s'%(pcolor[p],ampmarkstyle), markersize=markersize,markeredgewidth=markeredgewidth)
                                    newylimits =  recalcYlimits(plotrange,newylimits,gamp[p])
                          if (sum(xflag)>0):
                              myxrange = np.max(channels)-np.min(channels)
                              SetNewXLimits([np.min(channels)-myxrange/20, np.max(channels)+myxrange/20],1)
# #     # #                    print("amp: Resetting xaxis channel range to counteract flagged data")
                          if (xframe in bottomRowFrames or (xctr+1==len(antennasToPlot) and ispw==spwsToPlot[-1])):
                              pb.xlabel("Channels (%d)" % (len(pchannels[p])), size=mysize)  ### changed 2024Aug24
                      elif (xaxis.find('freq')>=0):   # amp
                          if (bOverlay):
                                myxrange = np.abs(xfrequencies[0]-xfrequencies[-1])
                                try:
                                    xrange2 = np.abs(xfrequencies2[0]-xfrequencies2[-1])
                                except:
                                    print("No amp data found in second solution.  Try increasing the solutionTimeThresholdSeconds above %.0f." % (solutionTimeThresholdSeconds))
                                    print("If this doesn't work, email the developer (%s)." % (developerEmail))
                                    return()

                                if (np.abs(myxrange/xrange2 - 1) > 0.05 + len(xflag)//len(xchannels)):  # 0.0666 is 2000/1875-1
                                   # These line widths are optimal for visualizing FDM over TDM
                                   width1 = 1
                                   width2 = 4
                                   # solutions differ in frequency width
                                   if (myxrange < xrange2):
                                      for p in range(nPolarizations):
                                            if (corrTypeToString(corr_type[p]) in polsToPlot):
                                                  pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                                  newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      for p in range(nPolarizations):
                                            if (corrTypeToString(corr_type[p]) in polsToPlot):
                                                  pb.plot(pfrequencies2[p], gamp2[p], '%s%s'%(p2color[p],ampmarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                                  newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                   else:
                                      for p in range(nPolarizations):
                                            if (corrTypeToString(corr_type[p]) in polsToPlot):
                                                  pb.plot(pfrequencies2[p], gamp2[p], '%s%s'%(p2color[p],ampmarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                                  newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                      for p in range(nPolarizations):
                                            if (corrTypeToString(corr_type[p]) in polsToPlot):
                                                  pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                                  newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                else:
                                   width1 = 1
                                   width2 = 2  # Just enough to distinguish one line from the other.
                                   # solutions may be different level of smoothing, so plot highest rms first
                                   if madOfDiff(gamp[0]) < madOfDiff(gamp2[0]): # and firstPlot != 1):  # only au version has this parameter
#                                   if (MAD(gamp[0]) < MAD(gamp2[0])):
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies2[p], gamp2[p], '%s%s'%(p2color[p],ampmarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                   else:
                                      # plot first solution first
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies2[p], gamp2[p], '%s%s'%(p2color[p],ampmarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                # must set new limits after plotting  'amp'
                                if (zoom=='intersect'):
                                    if (myxrange < xrange2):
                                        SetNewXLimits([min(xfrequencies[0],xfrequencies[-1])-myxrange*0.1, max(xfrequencies[0],xfrequencies[-1])+myxrange*0.1],2)
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies,
                                                  pfrequencies, ampMin, ampMax, xaxis, pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                    else:
# #     # #                              print("len(xfrequencies2) = ", len(xfrequencies2))
                                        SetNewXLimits([min(xfrequencies2[0],xfrequencies2[-1])-xrange2*0.1, max(xfrequencies2[0],xfrequencies2[-1])+xrange2*0.1],3)
                                        slstatus = SetLimits(plotrange, chanrange, newylimits, channels, frequencies2,
                                                  pfrequencies2, ampMin, ampMax, xaxis, pxl, chanrangeSetXrange,
                                                             chanrangePercent)
                                else:
                                    if (myxrange < xrange2):
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies,
                                                  pfrequencies, ampMin, ampMax, xaxis, pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                    else:
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies2,
                                                  pfrequencies2, ampMin, ampMax, xaxis, pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                # draw polarization and spw labels
                                if (xframe == firstFrame):
                                    # draw title including caltable name
                                    caltableList = 'c1=' + caltable + ', c2=' + caltable2 # + ' (%s)'%(utstring(uniqueTimes2[mytime],3))
                                    pb.text(xstartTitle, ystartTitle, caltableList, size=titlesize,
                                            color='k', transform=pb.gcf().transFigure)
                          elif (bpolyOverlay):
                              if (debug):
                                  print("in bpolyOverlay **********************************")
                              matches1 = []
                              if tableFormat >= 34:                            ### added 2024Aug
                                  scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
                              else:                                            ### added 2024Aug
                                  scansToPlotHere = scansToPlot                ### added 2024Aug
                              for tbp in range(len(timesBP)):
                                  if (sloppyMatch(uniqueTimes[mytime], timesBP[tbp], solutionTimeThresholdSeconds,
                                                  mytime, scansToPlotHere, scansForUniqueTimes,  # au version      ### modified 2024Aug
                                                  myprint=debugSloppyMatch
                                                  )):
                                      matches1.append(tbp)
                              matches1 = np.array(matches1)
                              if (len(matches1) < 1):
                                  print("No time match found between %.1f and %s" % (uniqueTimes[mytime], str(timesBP)))
                                  print("If you are sure the solutions correspond to the same data, you can set solutionTimeThresholdSeconds>=%.0f" % (1+np.ceil(np.abs(timesBP[0]-uniqueTimes[mytime]))))
                                  return()
                              matches2 = np.where(xant == np.array(antennasBP))[0]
                              if (len(matches2) < 1):
                                  print("No antenna match found: %s" % (str(xant), str(antennasBP)))
                              if (tableFormat == 33):
                                  matches3 = np.where(ispw == np.array(cal_desc_idBP))[0]
                                  if (len(matches3) < 1):
                                      print("No spw match found: %d not in %s" % (ispw, str(cal_desc_idBP)))
                              else:
                                  matches3 = np.where(ispw == np.array(spwBP))[0]
                                  if (len(matches3) < 1):
                                      print("No spw match found: %d not in %s" % (ispw, str(spwBP)))
                              matches12 = np.intersect1d(matches1,matches2)
                              if (len(matches12) < 1):
                                  print("No time+antenna match between: %s and %s" % (str(matches1), str(matches2)))
                              matches = np.intersect1d(matches12, matches3)
                              if (len(matches) < 1):
                                  print("No time+antenna+spw match between: %s and %s" % (str(matches12), str(matches3)))
                              try:
                                  index = matches[0]
                                  if (debug):
                                      print("Match = %d ***********************************" % (index))
                              except:
                                  print("No match found for time=%.6f, xant=%d, ispw=%d"  % (uniqueTimes[mytime],xant,ispw))
                                  print("antennasBP = %s" % (str(antennasBP)))
                                  print("cal_desc_idBP = %s" % (str(cal_desc_idBP)))
                                  timesBPstring = 'timesBP = '
                                  for i in timesBP:
                                      timesBPstring += "%.6f, " % i
                                  print(timesBPstring)
                                  return()
                              validDomain = [frequencyLimits[0,index], frequencyLimits[1,index]]
                              cc = calcChebyshev(polynomialAmplitude[index][0:nPolyAmp[index]], validDomain, frequenciesGHz[index]*1e+9)
                              if (debug): print("Done calcChebyshev 1")
                              fa = np.array(frequenciesGHz[index])
                              if (xfrequencies[0] < xfrequencies[-1]):
                                  matches = np.where(fa>xfrequencies[0])[0]
                                  matches2 = np.where(fa<xfrequencies[-1])[0]
                              else:
                                  matches = np.where(fa>xfrequencies[-1])[0]
                                  matches2 = np.where(fa<xfrequencies[0])[0]
                              if (len(matches) < 1):
                                  print("looking for %f-%f GHz inside %f-%f" % (xfrequencies[0],xfrequencies[-1],fa[0],fa[-1]))
                              amplitudeSolutionX = np.mean(gampx)*(cc-np.mean(cc)+1)

                              cc = calcChebyshev(polynomialAmplitude[index][nPolyAmp[index]:2*nPolyAmp[index]], validDomain, frequenciesGHz[index]*1e+9)
                              if (debug): print("Done calcChebyshev 2")
                              if (nPolarizations > 1):
                                if (yfrequencies[0] < yfrequencies[-1]):
                                  matches = np.where(fa>yfrequencies[0])[0]
                                  matches2 = np.where(fa<yfrequencies[-1])[0]
                                else:
                                  matches = np.where(fa>yfrequencies[-1])[0]
                                  matches2 = np.where(fa<yfrequencies[0])[0]
                                amplitudeSolutionY = np.mean(gampy)*(cc-np.mean(cc)+1)
                              if (bpolyOverlay2):
                                  validDomain = [frequencyLimits2[0,index], frequencyLimits2[1,index]]
                                  cc = calcChebyshev(polynomialAmplitude2[index][0:nPolyAmp2[index]],
                                                     validDomain, frequenciesGHz2[index]*1e+9)
                                  if (debug): print("Done calcChebyshev 3")
                                  fa = np.array(frequenciesGHz2[index])
                                  if (xfrequencies[0] < xfrequencies[-1]):
                                      matches = np.where(fa>xfrequencies[0])[0]
                                      matches2 = np.where(fa<xfrequencies[-1])[0]
                                  else:
                                      matches = np.where(fa>xfrequencies[-1])[0]
                                      matches2 = np.where(fa<xfrequencies[0])[0]
                                  amplitudeSolution2X = np.mean(gampx)*(cc-np.mean(cc)+1)

                                  cc = calcChebyshev(polynomialAmplitude2[index][nPolyAmp2[index]:2*nPolyAmp2[index]],
                                                     validDomain, frequenciesGHz2[index]*1e+9)
                                  if (debug): print("Done calcChebyshev 4")
                                  fa = np.array(frequenciesGHz2[index])
                                  if (yfrequencies[0] < yfrequencies[-1]):
                                      matches = np.where(fa>yfrequencies[0])[0]
                                      matches2 = np.where(fa<yfrequencies[-1])[0]
                                  else:
                                      matches = np.where(fa>yfrequencies[-1])[0]
                                      matches2 = np.where(fa<yfrequencies[0])[0]
                                  amplitudeSolution2Y = np.mean(gampy)*(cc-np.mean(cc)+1)
                                  if (debug):
                                      print("Done mean(gampy)")
                                  for p in range(nPolarizations):
                                      if (corrTypeToString(corr_type[p]) in polsToPlot):
                                          pb.plot(pfrequencies[p], gamp[p],'%s%s'%(pcolor[p],ampmarkstyle), markersize=markersize,markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                  if (debug): print("Done newylimits")
                                  if (corrTypeToString(corr_type[0]) in polsToPlot):
                                      pb.plot(frequenciesGHz[index], amplitudeSolutionX,'%s%s'%(p2color[0],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                      newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolutionX, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      pb.plot(frequenciesGHz2[index], amplitudeSolution2X, '%s%s'%(p3color[0],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                      newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolution2X, sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                  if (debug): print("Done newylimits2,3")
                                  if (nPolarizations == 2):
                                     if (debug): print("dualpol")
                                     if (corrTypeToString(corr_type[1]) in polsToPlot):
                                        pb.plot(frequenciesGHz[index], amplitudeSolutionY,'%s%s'%(p2color[1],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                        if (debug): print("A")
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolutionY, sideband,plotrange,ychannels,debug,12,chanrangePercent=chanrangePercent)
                                        if (debug): print("B")
                                        pb.plot(frequenciesGHz2[index], amplitudeSolution2Y, '%s%s'%(p3color[1],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                        if (debug): print("C")
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolution2Y, sideband,plotrange,ychannels2,debug,13,chanrangePercent=chanrangePercent)
                                  if (debug): print("Done this block")
                              else:
                                  for p in range(nPolarizations):
                                      if (corrTypeToString(corr_type[p]) in polsToPlot):
                                          pb.plot(pfrequencies[p], gamp[p],'%s%s'%(pcolor[p],ampmarkstyle), markersize=markersize,markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                  if (corrTypeToString(corr_type[0]) in polsToPlot):
                                      pb.plot(frequenciesGHz[index], amplitudeSolutionX,'%s%s'%(p2color[0],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                      newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolutionX, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                  if (nPolarizations == 2):
                                     if (corrTypeToString(corr_type[1]) in polsToPlot):
                                        pb.plot(frequenciesGHz[index], amplitudeSolutionY,'%s%s'%(p2color[1],bpolymarkstyle),markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, amplitudeSolutionY, sideband,plotrange,ychannels,chanrangePercent=chanrangePercent)
                              # endif (bpolyOverlay2)
                          else:
                              # we are not overlaying any B or polynomial solutions      'amp vs. freq'
                              if (showflagged):
                                  # Also show the flagged data to see where the flags are
                                  for p in range(nPolarizations):
                                    if (corrTypeToString(corr_type[p]) in polsToPlot):
                                      if (overlayAntennas or overlayTimes):
                                        pdesc1 = pb.plot(pfrequencies[p], gamp[p], '%s'%ampmarkstyles[p], markersize=markersize,markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                        if (overlayAntennas and overlayTimes==False):
                                            pb.setp(pdesc1, color=overlayColors[xctr])
                                        elif (overlayTimes and overlayAntennas==False):
                                            pb.setp(pdesc1, color=overlayColors[mytime])
                                        elif (overlayTimes and overlayAntennas): # try to support antenna,time
                                            if (myUniqueTime != []):
                                                pb.setp(pdesc1, color=overlayColors[myUniqueTime])
                                                # The third 'or' below is needed if pol='0' is flagged on antenna 0. -- 2012/10/12 (original spot)
                                                if (p==0 or len(polsToPlot)==1 or myUniqueColor==[]):
                                                    myUniqueColor.append(overlayColors[len(myUniqueColor)])
                                                pb.setp(pdesc1, color=myUniqueColor[-1])
                                      else:
                                        pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyles[p]), markersize=markersize,markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                              else:   # showing only unflagged data    'amp vs. freq'
                                  for p in range(nPolarizations):
                                    if (debug):
                                        print("*p=%d, polsToPlot=%s, len(fieldsToPlot)=%d, len(timerangeList)=%d, myUniqueTime=%s" % (p,str(polsToPlot),len(fieldsToPlot),len(timerangeList), str(myUniqueTime)))
                                    if (corrTypeToString(corr_type[p]) in polsToPlot):
                                      if (len(gamp[p]) == 0):  # Try this on Apr 2, 2012
# #     # #                                print("=============== Skipping flagged data on antenna %d = %s" % (xant,antstring))
                                          continue
                                      if (overlayAntennas or overlayTimes):
                                        pdesc = pb.plot(pfrequencies[p], gamp[p], '%s'%ampmarkstyles[p], markersize=markersize,markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                        if (overlayAntennas and overlayTimes==False):
                                            pb.setp(pdesc, color=overlayColors[xctr])
                                        elif (overlayTimes and overlayAntennas==False):
                                            pb.setp(pdesc, color=overlayColors[mytime])
                                        elif (overlayTimes and overlayAntennas):     #  try to support antenna,time
                                            if (myUniqueTime != []):
                                                pb.setp(pdesc, color=overlayColors[myUniqueTime])
                                                # The third 'or' below is needed if pol='0' is flagged on antenna 0. -- 2012/10/12 (original spot)
                                                if (p==0 or len(polsToPlot)==1 or myUniqueColor==[]):
                                                    myUniqueColor.append(overlayColors[len(myUniqueColor)])
                                                if (debug):
                                                    print("myUniqueColor = %s" % (str(myUniqueColor)))
                                                pb.setp(pdesc, color=myUniqueColor[-1])
                                      elif (overlaySpws):   # this elif block was missing prior to 2024Aug28
                                          mycolor = [xcolor,ycolor][p]
                                          linewidth = 1
                                          pdesc = pb.plot(pfrequencies[p], gamp[p], '%s'%(ampmarkstyles[0]), lw=linewidth, color=mycolor,
                                                          markersize=markersize,markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,
                                                                         plotrange,xchannels,debug,-18,chanrangePercent)
                                      else:  # show unflagged solutions, no overlay
                                         if (corrTypeToString(corr_type[p]) in polsToPlot):
                                            # since there is no overlay, don't use dashed line, so zero ------v
                                            pdesc = pb.plot(pfrequencies[p], gamp[p], '%s%s'%(pcolor[p],ampmarkstyles[0]),markersize=markersize,markeredgewidth=markeredgewidth)
                                            newylimits = recalcYlimitsFreq(chanrange, newylimits, gamp[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                            #                        print("newylimits for amp = ", newylimits)
#                                  if (debug): print("finished 'for' loop")
                                  if (sum(xflag)>0):
                                      # print("amp: Resetting xaxis frequency range to counteract flagged data")
                                      myxrange = np.max(frequencies)-np.min(frequencies)
                                      SetNewXLimits([np.min(frequencies)-0.15*myxrange, np.max(frequencies)+0.15*myxrange],4)

                          if (1==1 or (xframe in bottomRowFrames) or (xctr+1==len(antennasToPlot) and ispw==spwsToPlot[-1])):
                              # use 1==1 because spw might change between top row and bottom row of frames
                              pb.xlabel(xlabelString, size=mysize)
                      # endif (xaxis=='chan' elif xaxis=='freq'  for 'amp')
                      if (debug): print("finished 'if' block")
                      if (overlayTimes):
                          timeString =''
                      else:
                          if (len(uniqueTimes) > mytime):
                              timeString = ',  t%d/%d  %s' % (mytime,nUniqueTimes-1,utstring(uniqueTimes[mytime],3))
                              if (scansForUniqueTimes != []):
                                  if (scansForUniqueTimes[mytime]>=0):
                                      timeString = ',  scan%d  %s' % (scansForUniqueTimes[mytime],utstring(uniqueTimes[mytime],3))
                      spwString = buildSpwString(overlaySpws, overlayBasebands,
                                                 spwsToPlot, ispw, originalSpw[ispw],
                                                 observatoryName, baseband,
                                                 showBasebandNumber)
                      if (overlayTimes and len(fieldsToPlot) > 1):
                          indices = fstring = ''
                          for f in fieldIndicesToPlot:
                              if (f != fieldIndicesToPlot[0]):
                                  indices += ','
                                  fstring += ','
                              indices += str(uniqueFields[f])
                              if (msFound):
                                  fstring += msFields[uniqueFields[f]]
                          if (len(fstring) > fstringLimit):
                              fstring = fstring[0:fstringLimit] + '...'
                          titleString = "%sspw%s,  fields %s: %s%s" % (antennaString,spwString,
                                                                       indices, fstring, timeString)
                      else:
                          titleString = "%sspw%s,  field %d: %s%s" % (antennaString,spwString,uniqueFields[fieldIndex],
                                                                      fieldString,timeString)
                      tsize = titlesize-int(len(titleString)//(maxCharsBeforeReducingTitleFontSize//subplotCols))
                      pb.title(titleString, size=tsize)
                      if (abs(plotrange[0]) > 0 or abs(plotrange[1]) > 0):
                          SetNewXLimits([plotrange[0],plotrange[1]],5)
                      else:
                          # Here is 1st place where we eliminate white space on right and left edge of the plots: 'amp'
                          if (xaxis.find('chan')>=0):
                              SetNewXLimits([channels[0],channels[-1]],6)
                          else:
                              if (zoom != 'intersect'):
                                  if (overlaySpws or overlayBasebands):
                                      SetNewXLimits(frequencyRangeToPlotInBaseband[bbctr],7)
                                  else:
                                      SetNewXLimits([frequencies[0], frequencies[-1]],8)
                              if (bOverlay):
                                  if (xrange2 > myxrange+0.1 and zoom != 'intersect'):
                                      TDMisSecond = True
                      if (abs(plotrange[2]) > 0 or abs(plotrange[3]) > 0):
                          SetNewYLimits([plotrange[2],plotrange[3]])

                      xlim = pb.xlim()
                      ylim = pb.ylim()
                      ResizeFontsSetGrid(adesc,mysize)
                      pb.ylabel(yAmplitudeLabel, size=mysize)
                      pb.subplots_adjust(hspace=myhspace, wspace=mywspace)
                      myxrange = xlim[1]-xlim[0]
                      yrange = ylim[1]-ylim[0]
                      if (debug): print(("amp: ylim, yrange = ",  ylim, yrange))
                      if (overlayAntennas == False and overlayTimes == False and bOverlay == False and
                          ((overlaySpws == False and overlayBasebands == False) or spwctr==spwctrFirstToPlot)):
                          # draw polarization labels for no overlay, or overlaySpws/overlayBasebands
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          for p in range(nPolarizations):
                             if (corrTypeToString(corr_type[p]) in polsToPlot):
                               if spwctr==spwctrFirstToPlot or (not overlaySpws and not overlayBasebands):
                                   # no need to plot it more than once in the same position
                                   pb.text(x0, y0-subplotRows*p*0.03, corrTypeToString(corr_type[p]),
                                           color=pcolor[p],size=mysize, transform=pb.gca().transAxes)
                                   if (channeldiff > 0):
                                       pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                               corrTypeToString(corr_type[p])+' MAD = %.4f, St.Dev = %.4f'%(gamp_mad[p]['mad'],gamp_std[p]['std']),
                                               color=pcolor[p],size=mysize, transform=pb.gca().transAxes)
                          if (xframe == firstFrame):
                                # draw title including caltable name
                                caltableList = caltableTitle
                                if (bpolyOverlay):
                                      caltableList += ', ' + caltable2 + ' (degamp=%d, degphase=%d)'%(nPolyAmp[index]-1,nPolyPhase[index]-1)
                                      if (bpolyOverlay2):
                                            caltableList += ', ' + caltable3 + ' (degamp=%d, degphase=%d)'%(nPolyAmp2[index]-1,nPolyPhase2[index]-1)
                                pb.text(xstartTitle, ystartTitle, caltableList, size=titlesize,
                                        color='k', transform=pb.gcf().transFigure)

                      elif (overlayAntennas==True and xant==antennasToPlot[-1] and bOverlay == False   # ):
                            and overlayTimes==False):  # try to support antenna,time  avoid antenna labels 'amp'
                          # We do this last, because by then, the limits will be stable.
                          if (debug): print("overlayAntennas=True")
                          DrawAntennaNamesForOverlayAntennas(xstartPolLabel, ystartPolLabel, polsToPlot, corr_type, channeldiff, ystartMadLabel, subplotRows, gamp_mad, gamp_std, overlayColors, mysize, ampmarkstyle, markersize, markeredgewidth, msAnt, msFound, antennasToPlot, ampmarkstyle2, xframe, firstFrame, caltableTitle, titlesize)
                      elif (overlayTimes==True and bOverlay == False
                            and overlayAntennas==False):  # try to support antenna,time
                          doneOverlayTime = True  # assumed until proven otherwise in the 'for' loop
                          for f in fieldIndicesToPlot:
                            if (len(uniqueTimesPerFieldPerSpw[ispwInCalTable][f]) > 0):
                              # this spw/field combination had some entries plotted
                              if ((uniqueTimes[mytime] < uniqueTimesPerFieldPerSpw[ispwInCalTable][f][-1]-solutionTimeThresholdSeconds) and
                                  (uniqueTimes[mytime] < timerangeListTimes[-1])):
                                if tableFormat >= 34:                             #### added 2024Aug
                                  if scansForUniqueTimes[mytime] not in [scansForUniqueTimes[-1],scansToPlot[-1]] and mytime != timerangeList[-1]:  # fix for CAS-14096: if we are on the final time then we are done;  and add support for specifying a limited set of timeranges via the timeranges parameter     ### added 2024Aug
                                    if (debug):
                                        print("-----------Not done because %.0f < %.0f-%d for fieldIndex=%d and <%.0f and scan%d not in scan[%d,%d] and t%d != t%d" % (uniqueTimes[mytime], uniqueTimesPerFieldPerSpw[ispwInCalTable][f][-1], solutionTimeThresholdSeconds, f, timerangeListTimes[-1],scansForUniqueTimes[mytime],scansForUniqueTimes[-1],scansToPlot[-1],mytime,timerangeList[-1]))
                                        print("-----------ispwInCalTable=%d, mytime=%d, len(uniqueTimes) = %d" % (ispwInCalTable, mytime, len(uniqueTimes)))
                                    doneOverlayTime = False
                                else:                                              #### added 2024Aug
                                    doneOverlayTime = False                        #### added 2024Aug
                          if (debug):
                              print("------doneOverlayTime = %s on mytime %d" % (str(doneOverlayTime),mytime))
                          if (doneOverlayTime):
                          # either it is the last time of any times in solution, or the last time in the list of times to plot
                              if (debug):
                                  print("*** on last time = %d for last fieldIndex %d  or %d>=%d" % (mytime,fieldIndex,mytime,timerangeList[-1]))
                              mytime = nUniqueTimes-1
                              # We do this last, because by then, the limits will be broad enought and stable.
                              # draw polarization labels
                              DrawPolarizationLabelsForOverlayTime(xstartPolLabel,ystartPolLabel,corr_type,polsToPlot,
                                                                   channeldiff,ystartMadLabel,subplotRows,gamp_mad,mysize,
                                                                   ampmarkstyle,markersize,ampmarkstyle2, gamp_std)
                              if (xframe == firstFrame):
                                  # draw title including caltable name
                                  pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize,
                                          color='k', transform=pb.gcf().transFigure)
                                  if tableFormat >= 34:                           ### added 2024Aug
                                      scansToPlotHere = scansToPlotPerSpw[ispw]   ### added 2024Aug
                                  else:                                           ### added 2024Aug
                                      scansToPlotHere = scansToPlot               ### added 2024Aug
                                  if (debug): print("drawOverlayTimeLegends loc 2")
                                  drawOverlayTimeLegends(xframe, firstFrame, xstartTitle, ystartTitle,
                                                         caltable, titlesize, fieldIndicesToPlot,
                                                         ispwInCalTable, uniqueTimesPerFieldPerSpw,
                                                         timerangeListTimes, solutionTimeThresholdSeconds,
                                                         debugSloppyMatch, ystartOverlayLegend, debug, mysize,
                                                         fieldsToPlot, myUniqueColor,timeHorizontalSpacing,
                                                         fieldIndex, overlayColors, antennaVerticalSpacing,
                                                         overlayAntennas, timerangeList, caltableTitle,
                                                         mytime, scansToPlotHere, scansForUniqueTimes,
                                                         uniqueSpwsInCalTable, uniqueTimes)   ### modified 2024Aug
                      elif (overlayAntennas and overlayTimes):  # Oct 23, 2012
                          # This will only happen for overlay='antenna,time'
                          if (xframe == firstFrame and mytime == firstTimeMatch and xctr==firstUnflaggedAntennaToPlot and bOverlay==False):  # bug fix on 2015-08-19 for CAS-7820
                              # draw title including caltable name
                              pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k',
                                      transform=pb.gcf().transFigure)
                              DrawBottomLegendPageCoords(msName, uniqueTimes[mytime], mysize, figfile)
                          # Adding the following 'for' loop on Mar 13, 2013 to support the case of
                          # single time range with overlay='antenna,time'
                          if (xant==antennasToPlot[-1]):
                            doneOverlayTime = True  # assumed until proven otherwise in the 'for' loop
                            for f in fieldIndicesToPlot:
                                if (len(uniqueTimesPerFieldPerSpw[ispwInCalTable][f]) > 0):
                                    if ((uniqueTimes[mytime] < uniqueTimesPerFieldPerSpw[ispwInCalTable][f][-1]-solutionTimeThresholdSeconds) and
                                        (uniqueTimes[mytime] < timerangeListTimes[-1])):
                                      if tableFormat >= 34:                             #### added 2024Aug
#                                        if (scansForUniqueTimes[mytime] != scansForUniqueTimes[-1]) #### added 2024Aug  (fix for CAS-14096): if we are on the final time then we are done
                                       if scansForUniqueTimes[mytime] != scansForUniqueTimes[-1] and mytime != timerangeList[-1]:  # fix for CAS-14096: if we are on the final time then we are done;  and add support for specifying a limited set of timeranges via the timeranges parameter     ### added 2024Aug
                                          if (debug):
                                            print("-----------Not done because %.0f < %.0f-%d for fieldIndex=%d and <%.0f" % (uniqueTimes[mytime], uniqueTimesPerFieldPerSpw[ispwInCalTable][f][-1], solutionTimeThresholdSeconds, f, timerangeListTimes[-1]))
                                            print("-----------ispwInCalTable=%d, mytime=%d, len(uniqueTimes) = %d" % (ispwInCalTable, mytime, len(uniqueTimes)))
                                          doneOverlayTime = False
                                      else:                                     #### added 2024Aug
                                          doneOverlayTime = False               #### added 2024Aug
                            if (doneOverlayTime):
                                # This is necessary for the case that no antennas were flagged for the single timerange selected
                                if (debug): print("drawOverlayTimeLegends loc 3")
                                drawOverlayTimeLegends(xframe,firstFrame,xstartTitle,ystartTitle,caltable,titlesize,
                                                       fieldIndicesToPlot,ispwInCalTable,uniqueTimesPerFieldPerSpw,
                                                       timerangeListTimes, solutionTimeThresholdSeconds,
                                                       debugSloppyMatch,ystartOverlayLegend,debug,mysize,
                                                       fieldsToPlot,myUniqueColor,timeHorizontalSpacing,
                                                       fieldIndex, overlayColors, antennaVerticalSpacing,
                                                       overlayAntennas, timerangeList, caltableTitle,
                                                       mytime, scansToPlotPerSpw[ispw], scansForUniqueTimes,
                                                       uniqueSpwsInCalTable, uniqueTimes)

                      # endif / elif / elif / elif

                      if (debug): print("####### 2nd place")
                      # Here is 2nd place where we eliminate any white space on the right and left edge of the plots: 'amp'
                      #
                      if (abs(plotrange[2]) > 0 or abs(plotrange[3]) > 0):
                          SetNewYLimits([plotrange[2],plotrange[3]])
                      if (plotrange[0]==0 and plotrange[1]==0):
                          if (xaxis.find('chan')>=0):
                              SetNewXLimits([channels[0],channels[-1]],9)
                          else:
                              if (zoom != 'intersect'):
                                  if (overlaySpws or overlayBasebands):
                                      SetNewXLimits(frequencyRangeToPlotInBaseband[bbctr],10)
                                  else:
                                      SetNewXLimits([frequencies[0], frequencies[-1]],11)
                              if (bOverlay):
# #     # #                        print("Checking if %f >= %f" % (xrange2, myxrange))
                                  if (xrange2 >= myxrange and zoom != 'intersect'):
                                      # This is necessary if caltable2=TDM and caltable=FDM
                                      SetNewXLimits([frequencies2[0], frequencies2[-1]],12)
                                  if (xrange2 > myxrange+0.1 and zoom != 'intersect'):
                                      TDMisSecond = True
                      else:
                          SetNewXLimits([plotrange[0], plotrange[1]],13)
                      if (debug): print("done SetNewXLimits")

                      # I need the following line for chanrange to work
                      if (chanrange[0] != 0 or chanrange[1] != 0 or chanrangePercent != None):
                          SetLimits(plotrange, chanrange, newylimits, channels, frequencies, pfrequencies,
                                    ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                    chanrangePercent)

                      # Finally, draw the atmosphere and FDM windows, if requested.  'amp'
                      if ((overlayAntennas==False and overlayTimes==False) or
                          (overlayAntennas==True and overlayTimes==False and xant==antennasToPlot[-1]) or
                          (overlayTimes==True and overlayAntennas==False and doneOverlayTime) or
                          (overlayTimes and overlayAntennas and  # Aug 5, 2013
                           xant==antennasToPlot[-1] and doneOverlayTime and mytime==finalTimeMatch # 2015-08-19 for CAS-7820
                           and not drewAtmosphere)  # added on 2014-12-04 to support case of a flagged antenna CAS-7187
                          ):
                          if ((showatm or showtsky) and len(atmString) > 0):
                              DrawAtmosphere(showatm, showtsky, subplotRows, atmString,
                                             mysize, TebbSky, plotrange, xaxis, atmchan,
                                             atmfreq, transmission, subplotCols,
                                             showatmPoints=showatmPoints, xframe=xframe,
                                             channels=channels,mylineno=lineNumber(),
                                             overlaySpws=overlaySpws,
                                             overlayBasebands=overlayBasebands,
                                             drewAtmosphere=drewAtmosphere,loc=203,
                                             showtsys=showtsys, Trx=Trx)
                              if (LO1):
                                  # Now draw the image band
                                  DrawAtmosphere(showatm,showtsky, subplotRows, atmString,
                                                 mysize, TebbSkyImage, plotrange, xaxis,
                                                 atmchanImage, atmfreqImage, transmissionImage,
                                                 subplotCols, LO1, xframe, firstFrame,
                                                 showatmPoints,
                                                 channels=channels,mylineno=lineNumber(),
                                                 overlaySpws=overlaySpws,
                                                 overlayBasebands=overlayBasebands,
                                                 drewAtmosphere=drewAtmosphere,loc=204,
                                                 showtsys=showtsys, Trx=Trx)
                              drewAtmosphere = True
                          if (xaxis.find('freq')>=0 and showfdm and nChannels <= 256):
                              if (tableFormat == 33):
                                  showFDM(originalSpw_casa33, chanFreqGHz_casa33, baseband, showBasebandNumber, basebandDict, overlayColors)
                              else:
                                  showFDM(originalSpw, chanFreqGHz, baseband, showBasebandNumber, basebandDict, overlayColors)
                      if (debug): print("done drawAtmosphere/FDM check")

                      if (bOverlay):
                          # draw polarization labels
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          for p in range(nPolarizations):
                              if (corrTypeToString(corr_type[p]) in polsToPlot):
                                  pb.text(x0, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p])+'-c1',
                                          color=pcolor[p],size=mysize,transform=pb.gca().transAxes)
                                  pb.text(x0, y0-p*0.03*subplotRows-0.06*subplotRows, corrTypeToString(corr_type[p])+'-c2',
                                          color=p2color[p],size=mysize,transform=pb.gca().transAxes)
                      if (debug): print("done pol labels")
                      if (bpolyOverlay and xaxis.find('freq')>=0):
                          # draw polarization labels
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          if (x2color != xcolor):
                                for p in range(nPolarizations):
                                    if (corrTypeToString(corr_type[0]) in polsToPlot):
                                        pb.text(x0+0.1, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p]), color=p2color[p],
                                                size=mysize,transform=pb.gca().transAxes)
                          if (bpolyOverlay2):
                                for p in range(nPolarizations):
                                      if (corrTypeToString(corr_type[0]) in polsToPlot):
                                            pb.text(x0+0.2, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p]),
                                                    color=p3color[p], size=mysize,transform=pb.gca().transAxes)

                      myIndexTime = uniqueTimesPerFieldPerSpw[ispwInCalTable][fieldIndex][-1]
                      if (debug): print("running sloppyMatch")
                      if tableFormat >= 34:                           ### added 2024Aug
                          scansToPlotHere = scansToPlotPerSpw[ispw]   ### added 2024Aug
                      else:                                           ### added 2024Aug
                          scansToPlotHere = scansToPlot               ### added 2024Aug
                      matched,mymatch = sloppyMatch(myIndexTime,uniqueTimes,solutionTimeThresholdSeconds,
                                                    mytime, scansToPlotHere,      ### modified 2024Aug
                                                    scansForUniqueTimes,
                                                    whichone=True,myprint=debug)
                      if (debug):
                          print("1)done sloppyMatch, mytime=%d, scansForUniqueTimes=%s" % (mytime,str(scansForUniqueTimes)))
                          print("ispw=%d" % (ispw))
                          print("len(scansToPlotPerSpw)=%d" % (len(scansToPlotPerSpw)))
                      if (matched == False and scansForUniqueTimes[mytime] in scansToPlotPerSpw[ispw]):
                          print("---------- 1) Did not find %f in %s" % (myIndexTime,str(uniqueTimes)))
                          print("Try re-running with a smaller solutionTimeThresholdSeconds (currently %f)" % (solutionTimeThresholdSeconds))
                          return
                      else:
                          # we are on the final time to be plotted
                          if (debug): print("on the final time")
                          mytimeTest = mytime==nUniqueTimes-1 # mytime==myIndexTime  # mytime==mymatch
                      if ((xframe == 111 and amplitudeWithPhase) or
                          # Following case is needed to make subplot=11 to work for: try to support overlay='antenna,time'
                          (xframe == lastFrame and overlayTimes and overlayAntennas and
                           xctr+1==len(antennasToPlot) and
                           mytimeTest and
                           spwctr<len(spwsToPlot))):  # removed +1 from spwctr+1 on 2014-04-05 to match au
                               if (debug):
                                   print("xframe=%d  ==  lastFrame=%d,  amplitudeWithPhase=%s" % (xframe, lastFrame, str(amplitudeWithPhase)))
                                   print("xctr+1=%d == len(antennasToPlot)=%d"  % (xctr+1,len(antennasToPlot)))
                                   print("mytime+1=%d == len(uniqueTimes)=%d"  % (mytime+1,len(uniqueTimes)))
                                   print("spwctr+1=%d < len(spwsToPlot)=%d"  % (spwctr+1,len(spwsToPlot)))
                               if (len(figfile) > 0):
                                     plotfiles.append(makeplot(figfile,msFound,msAnt,
                                                               overlayAntennas,pages,pagectr,
                                                               density,interactive,antennasToPlot,
                                                               spwsToPlot,overlayTimes,overlayBasebands,
                                                               3,xant,ispw,subplot,resample,
                                                               debug,figfileSequential,figfileNumber))
                                     figfileNumber += 1

                               donetime = time.time()
                               drewAtmosphere = False # needed for CAS-7187 (subplot=11)
                               if (interactive):
                                  pb.draw()
                                  myinput = input("Press return for next page (b for backwards, q to quit): ")
                               else:
                                  myinput = ''
                               skippingSpwMessageSent = 0
                               mytimestamp = time.time()
                               if (myinput.find('q') >= 0):
                                   showFinalMessage(overlayAntennas, solutionTimeSpread, nUniqueTimes)
                                   return()
                               if (myinput.find('b') >= 0):
                                   if (pagectr > 0):
                                       if (debug):
                                           print("Decrementing pagectr from %d to %d" % (pagectr, pagectr-1))
                                       pagectr -= 1
                                   else:
                                       if (debug):
                                           print("Not decrementing pagectr=%d" % (pagectr))

                                   redisplay = True
                                   #redisplay the current page by setting ctrs back to the value they had at start of that page
                                   xctr = pages[pagectr][PAGE_ANT]
                                   spwctr = pages[pagectr][PAGE_SPW]
                                   mytime = pages[pagectr][PAGE_TIME]
                                   myap = pages[pagectr][PAGE_AP]
                                   xant = antennasToPlot[xctr]
                                   antstring, Antstring = buildAntString(xant,msFound,msAnt)
                                   ispw = spwsToPlot[spwctr]
# #     # #                         print("Returning to [%d,%d,%d,%d]" % (xctr,spwctr,mytime,myap))
                                   if (xctr==pages[0][PAGE_ANT] and spwctr==pages[0][PAGE_SPW] and mytime==pages[0][PAGE_TIME] and pages[0][PAGE_AP]==myap):
                                     safe_pb_clf()
                                     if (debug):
                                         print("2)Setting xframe to %d" % xframeStart)
                                     xframe = xframeStart
                                     myUniqueColor = []
                                     continue
                               else:
                                   pagectr += 1
                                   if (pagectr >= len(pages)):
                                       if (xframe == lastFrame and overlayTimes and overlayAntennas and xctr+1==len(antennasToPlot) and
                                           yaxis=='amp'):
                                           # I'm not sure why this works, but is needed to fix CAS-7154
                                           myspwctr = spwctr+1
                                       else:
                                           myspwctr = spwctr
                                       pages.append([xctr,myspwctr,mytime,1])
                                       if (debug):
                                           print("amp: appending [%d,%d,%d,%d]" % (xctr,myspwctr,mytime,1))
                                       newpage = 0
                               safe_pb_clf()
                               if (debug):
                                   print("3)Setting xframe to %d" % xframeStart)
                               xframe = xframeStart
                               myUniqueColor = []
                      else:
                          if (debug):
                              print("::: Not done page: Not checking whether we need to set xframe=xframeStart")
                              print("::: xframe=%d  ?=  lastFrame=%d,  amplitudeWithPhase=" % (xframe, lastFrame), amplitudeWithPhase)
                              print("::: xctr+1=%d ?= len(antennasToPlot)=%d"  % (xctr+1,len(antennasToPlot)))
                              print(":::: mytimeTest = %s"  % (mytimeTest))
                              print("::: spwctr=%d ?< len(spwsToPlot)=%d"  % (spwctr,len(spwsToPlot)))
###########################################################
################### Here is the phase plotting ############
###########################################################
                  if (yaxis.find('phase')>=0 or amplitudeWithPhase) and doneOverlayTime==False:
                      if (channeldiff > 0):
                          pchannels = [xchannels,ychannels]  # this is necessary because np.diff reduces nchan by 1
                          pfrequencies = [xfrequencies,yfrequencies]  # this is necessary because np.diff reduces nchan by 1
                          if (bOverlay):
                              pchannels2 = [xchannels2,ychannels2]  # this is necessary because np.diff reduces nchan by 1
                              pfrequencies2 = [xfrequencies2,yfrequencies2]  # this is necessary because np.diff reduces nchan by 1
                      if (overlayTimes == False or mytime==firstTimeMatch):
                        if ((overlaySpws == False and overlayBasebands==False) or spwctr==spwctrFirstToPlot or
                            spwctr>spwsToPlot[-1] or
                            (overlayBasebands and amplitudeWithPhase)): # CAS-6477
                          if (overlayAntennas==False or xctr==firstUnflaggedAntennaToPlot
                              or xctr>antennasToPlot[-1]):  # 2012-05-24, to fix the case where all ants flagged on one timerange
                              xframe += 1
                              if debug:
                                  print("u) incrementing xframe to %d" % xframe)
                              myUniqueColor = []
                              newylimits = [LARGE_POSITIVE, LARGE_NEGATIVE]
                              if (phase != ''):
                                  if ((phase[0] != 0 or phase[1] != 0) and amplitudeWithPhase):
                                      newylimits = phase
                      if (debug):
                          print("$$$$$$$$$$$$$$$$$$$$$$$  ready to plot phase on xframe %d" % (xframe))


                      if (previousSubplot != xframe):
                          adesc = safe_pb_subplot(xframe)  # avoid deprecation warning in CASA6 when xframe already was opened
                          drewAtmosphere = False
                      previousSubplot = xframe
                      gphsx = np.arctan2(np.imag(gplotx),np.real(gplotx))*180.0/math.pi
                      if (nPolarizations == 2):
                          gphsy = np.arctan2(np.imag(gploty),np.real(gploty))*180.0/math.pi
                          if (channeldiff>0):
                              if (xaxis == 'chan'):
                                  gphs0, newx0, gphs0res, newx0res = channelDifferences(gphsx, pchannels[0], resample)
                                  gphs1, newx1, gphs1res, newx1res = channelDifferences(gphsy, pchannels[1], resample)
                                  pchannels = [newx0,newx1]
                              else:
                                  gphs0, newx0, gphs0res, newx0res = channelDifferences(gphsx, pfrequencies[0], resample)
                                  gphs1, newx1, gphs1res, newx1res  = channelDifferences(gphsy, pfrequencies[1], resample)
                                  pfrequencies = [newx0,newx1]
                              gphs = [gphs0, gphs1]
                              gphsres = [gphs0res, gphs1res]
                              gphs_mad = [madInfo(gphs[0],madsigma,edge), madInfo(gphs[1],madsigma,edge)]
                              gphs_std = [stdInfo(gphsres[0],madsigma,edge,ispw,xant,0), stdInfo(gphsres[1],madsigma,edge,ispw,xant,1)]
                              for p in [0,1]:
                                  madstats[Antstring][ispw][mytime][p]['phase'] = gphs_mad[p]['mad']
                                  madstats[Antstring][ispw][mytime][p]['phasestd'] = gphs_std[p]['std']
                                  if (gphs_mad[p]['nchan'] > 0):
                                      checkAbsSum = np.sum(np.abs(gphs[p]))
                                      if (checkAbsSum < PHASE_ABS_SUM_THRESHOLD):
                                          if (debug): print("%s, Pol %d, spw %d, %s, phs: not printing because abs sum of all values near zero (%f)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), checkAbsSum))
                                      else:
                                          casalogPost(debug, "%s, Pol %d, spw %2d, %s, phs: %4d points exceed %.1f sigma (worst=%.2f at chan %d)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), gphs_mad[p]['nchan'], madsigma, gphs_mad[p]['outlierValue'], gphs_mad[p]['outlierChannel']+pchannels[p][0]))
                          else:
                              gphs = [gphsx,gphsy]
                      else:  # 1-pol
                          if (channeldiff>0):
                              if (xaxis == 'chan'):
                                  gphs0, newx0, gphs0res, newx0res = channelDifferences(gphsx, pchannels[0], resample)
                                  pchannels = [newx0]
                              else:
                                  gphs0, newx0, gphs0res, newx0res = channelDifferences(gphsx, pfrequencies[0], resample)
                                  pfrequencies = [newx0]
                              gphs = [gphs0]
                              gphsres = [gphs0res]
                              p = 0
                              gphs_mad = [madInfo(gphs[p], madsigma, edge)]
                              gphs_std = [stdInfo(gphsres[p], madsigma, edge, ispw,xant,p)]
                              madstats[Antstring][ispw][mytime][p]['phase'] = gphs_mad[p]['mad']
                              madstats[Antstring][ispw][mytime][p]['phasestd'] = gphs_mad[p]['std']
                              if (gphs_mad[p]['nchan'] > 0):
                                  checkAbsSum = np.sum(np.abs(gphs[p]))
                                  if (checkAbsSum < PHASE_ABS_SUM_THRESHOLD):
                                      if (debug): print("%s, Pol %d, spw %d, %s, phs: not printing because all values near zero (%f)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), checkAbsSum))
                                  else:
                                      casalogPost(debug, "%s, Pol %d, spw %2d, %s, phs: %4d points exceed %.1f sigma (worst=%.2f at chan %d)" % (Antstring, p, ispw, utstring(uniqueTimes[mytime],0), gphs_mad[p]['nchan'], madsigma, gphs_mad[p]['outlierValue'], gphs_mad[p]['outlierChannel']+pchannels[p][0]))
                          else:
                              gphs = [gphsx]
                      if (bOverlay):
                            if (debug):
                                print("computing phase for second table")
                            gphsx2 = np.arctan2(np.imag(gplotx2),np.real(gplotx2))*180.0/math.pi
                            if (nPolarizations == 2):
                                gphsy2 = np.arctan2(np.imag(gploty2),np.real(gploty2))*180.0/math.pi
                                if (channeldiff>0):
                                    if (xaxis == 'chan'):
                                        gphs2_0, newx0, gphs2_0res, newx0res = channelDifferences(gphsx2, pchannels2[0], resample)
                                        gphs2_1, newx1, gphs2_1res, newx1res  = channelDifferences(gphsy2, pchannels2[1], resample)
                                        pchannels2 = [newx0, newx1]
                                    else:
                                        gphs2_0, newx0, gphs2_0res, newx0res = channelDifferences(gphsx2, pfrequencies2[0], resample)
                                        gphs2_1, newx1, gphs2_1res, newx1res = channelDifferences(gphsy2, pfrequencies2[1], resample)
                                        pfrequencies2 = [newx0, newx1]
                                    gphs2 = [gphs2_0, gphs2_1]
                                    gphs2res = [gphs2_0res, gphs2_1res]
                                else:
                                    gphs2 = [gphsx2, gphsy2]
                            else:
                                if (channeldiff>0):
                                    if (xaxis == 'chan'):
                                        gphs2_0, newx0, gphs2_0res, newx0res = channelDifferences(gphsx2, pchannels2[0], resample)
                                        pchannels2 = [newx0]
                                    else:
                                        gphs2_0, newx0, gphs2_0res, newx0res = channelDifferences(gphsx2, pfrequencies2[0], resample)
                                        pfrequencies2 = [newx0]
                                    gphs2 = [gphs2_0]
                                    gphs2res = [gphs2_0res]
                                else:
                                    gphs2 = [gphsx2]
                      else:
                            if (debug):
                                print("bOverlay is FALSE ===========================")

                      if (xaxis.find('chan')>=0 or len(xfrequencies) < 1):    # 'phase'
                          for p in range(nPolarizations):
                            if (corrTypeToString(corr_type[p]) in polsToPlot):
                              if (overlayAntennas or overlayTimes):
                                  pdesc = pb.plot(pchannels[p],gphs[p],'%s'%(phasemarkstyles[p]),markersize=markersize,markeredgewidth=markeredgewidth)
                                  newylimits =  recalcYlimits(plotrange,newylimits,gphs[p])  # 10/27/2011
                                  if (newylimits[1]-newylimits[0] < minPhaseRange):
                                      newylimits = [-minPhaseRange,minPhaseRange]
                                  if (phase != ''):
                                      if ((phase[0] != 0 or phase[1] != 0) and amplitudeWithPhase):
                                          newylimits = phase

                                  if (overlayAntennas and overlayTimes==False):
                                      pb.setp(pdesc, color=overlayColors[xctr])
                                  elif (overlayTimes and overlayAntennas==False):
                                      pb.setp(pdesc, color=overlayColors[mytime])
                                  elif (overlayTimes):   # try to support antenna,time
                                      if (myUniqueTime != []):
                                          pb.setp(pdesc, color=overlayColors[myUniqueTime])
                                          # The third 'or' below is needed if pol='0' is flagged on antenna 0. -- 2012/10/12 (original spot)
                                          if (p==0 or len(polsToPlot)==1 or myUniqueColor==[]):
                                              myUniqueColor.append(overlayColors[len(myUniqueColor)])
                                          pb.setp(pdesc, color=myUniqueColor[-1])
                              else:
                                  pb.plot(pchannels[p],gphs[p],'%s%s'%(pcolor[p],phasemarkstyles[0]), markersize=markersize,markeredgewidth=markeredgewidth)
                                  newylimits =  recalcYlimits(plotrange,newylimits,gphs[p]) # 10/27/2011
                                  if (newylimits[1]-newylimits[0] < minPhaseRange):
                                      newylimits = [-minPhaseRange,minPhaseRange]
                                  if (phase != ''):
                                      if ((phase[0] != 0 or phase[1] != 0) and amplitudeWithPhase):
                                          newylimits = phase
                          if (sum(xflag)>0):
# #     # #                    print("phase: Resetting xaxis channel range to counteract flagged data")
                              myxrange = np.max(channels)-np.min(channels)
                              SetNewXLimits([np.min(channels)-myxrange/20,
                                             np.max(channels)+myxrange/20],14)
                          if (xframe in bottomRowFrames or (xctr+1==len(antennasToPlot) and ispw==spwsToPlot[-1])):
                              pb.xlabel("Channels (%d)"%(len(pchannels[p])), size=mysize)  ### changed 2024Aug24
                      elif (xaxis.find('freq')>=0):     # 'phase'
                          if (bOverlay):
                                if (debug):
                                    p = 0    ### added 2024Aug to prevent crash due to undefined variable
                                    print("Preparing to plot phase from %f-%f for pols: %s" % (xfrequencies[0],xfrequencies[-1],str(polsToPlot)))
                                    print("Preparing to plot phase from %f-%f for pols: %s" % (pfrequencies[p][0],pfrequencies[p][-1],str(polsToPlot)))
                                    print("Preparing to plot phase from %f-%f for pols: %s" % (pfrequencies2[p][0],pfrequencies2[p][-1],str(polsToPlot)))
                                myxrange = np.abs(xfrequencies[0]-xfrequencies[-1])
                                try:
                                    xrange2 = np.abs(xfrequencies2[0]-xfrequencies2[-1])
                                except:
                                    print("No phase data found in second solution.  Try increasing the solutionTimeThresholdSeconds above %.0f." % (solutionTimeThresholdSeconds))
                                    print("If this doesn't work, email the developer (%s)." % (developerEmail))
                                    return()
                                if (np.abs(myxrange/xrange2 - 1) > 0.05 + len(xflag)//len(xchannels)):  # 0.0666 is 2000/1875-1
                                   # These line widths are optimal for visualizing FDM over TDM
                                   width1 = 1
                                   width2 = 4
                                   # solutions differ in frequency width, so show the narrower one first
                                   if (myxrange < xrange2):
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                          if (debug): print("pb.plot 1")
                                          pb.plot(pfrequencies[p], gphs[p], '%s%s'%(pcolor[p],phasemarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                          if (debug): print("pb.plot 2")
                                          pb.plot(pfrequencies2[p], gphs2[p], '%s%s'%(p2color[p],phasemarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                   else:
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                           if (debug): print("pb.plot 3")
                                           pb.plot(pfrequencies2[p], gphs2[p], '%s%s'%(p2color[p],phasemarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                           newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                           if (debug): print("pb.plot 4")
                                           pb.plot(pfrequencies[p], gphs[p], '%s%s'%(pcolor[p],phasemarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                           newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                else:
                                   width1 = 1
                                   width2 = 1
                                   # solutions may be different level of smoothing, so plot highest rms first
                                   if (MAD(gphsx) < MAD(gphsx2)):
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                         if (debug): print("pb.plot 5")
                                         pb.plot(pfrequencies2[p], gphs2[p], '%s%s'%(p2color[p],phasemarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                         newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                         if (debug): print("pb.plot 6")
                                         pb.plot(pfrequencies[p], gphs[p], '%s%s'%(pcolor[p],phasemarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                         newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                   else:
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                         if (debug): print("pb.plot 7")
                                         pb.plot(pfrequencies[p], gphs[p], '%s%s'%(pcolor[p],phasemarkstyle), linewidth=width2, markersize=markersize,markeredgewidth=markeredgewidth)
                                         newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                     for p in range(nPolarizations):
                                       if (corrTypeToString(corr_type[p]) in polsToPlot):
                                         if (debug): print("pb.plot 9")
                                         pb.plot(pfrequencies2[p], gphs2[p], '%s%s'%(p2color[p],phasemarkstyle), linewidth=width1, markersize=markersize,markeredgewidth=markeredgewidth)
                                         newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs2[p], sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                # must set new limits after plotting  'phase'
                                (y0,y1) = pb.ylim()
                                if (y1-y0 < minPhaseRange):
                                      # this must come before defining ticks
                                      SetNewYLimits([-minPhaseRange,minPhaseRange])
                                if (zoom=='intersect'):
                                    if (myxrange < xrange2):
                                        SetNewXLimits([min(xfrequencies[0],xfrequencies[-1])-myxrange*0.1, max(xfrequencies[0],xfrequencies[-1])+myxrange*0.1],15)
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies,
                                                  pfrequencies, ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                    else:
                                        SetNewXLimits([min(xfrequencies2[0],xfrequencies2[-1])-xrange2*0.1, max(xfrequencies2[0],xfrequencies2[-1])+xrange2*0.1],16)
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies2,
                                                  pfrequencies2, ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                else:
                                    if (myxrange < xrange2):
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies,
                                                  pfrequencies, ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                    else:
                                        SetLimits(plotrange, chanrange, newylimits, channels, frequencies2,
                                                  pfrequencies2, ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                                  chanrangePercent)
                                # draw polarization and spw labels
                                if (xframe == firstFrame):
                                    # draw title including caltable name
                                    caltableList = 'c1=' + caltable + ', c2=' + caltable2 # + ' (%s)'%(utstring(uniqueTimes2[mytime],3))
                                    pb.text(xstartTitle, ystartTitle, caltableList, size=titlesize,
                                            color='k', transform=pb.gcf().transFigure)
                          elif (bpolyOverlay):
                                  matches1 = []
                                  if tableFormat >= 34:                           ### added 2024Aug
                                      scansToPlotHere = scansToPlotPerSpw[ispw]   ### added 2024Aug
                                  else:                                           ### added 2024Aug
                                      scansToPlotHere = scansToPlot               ### added 2024Aug
                                  for tbp in range(len(timesBP)):
                                      if (sloppyMatch(uniqueTimes[mytime], timesBP[tbp], solutionTimeThresholdSeconds,
                                                      mytime, scansToPlotHere, scansForUniqueTimes,  ### modified 2024Aug
                                                      myprint=debugSloppyMatch)):
                                          matches1.append(tbp)
                                  matches1 = np.array(matches1)
# #     # #                        print("time matches: matches1 = ", matches1)
                                  if (len(matches1) < 1):
                                      print("No time match found")
                                      print("If you are sure the solutions correspond to the same data, you can set solutionTimeThresholdSeconds=%.0f" % (1+np.ceil(np.abs(timesBP[0]-uniqueTimes[mytime]))))
                                      return()
                                  matches2 = np.where(xant == np.array(antennasBP))[0]
                                  if (len(matches2) < 1):
                                      print("No antenna match found between %s and %s" % (str(xant), str(antennasBP)))
# #     # #                        print("antenna matches: matches2 = ", matches2)

                                  if (tableFormat == 33):
                                      matches3 = np.where(ispw == np.array(cal_desc_idBP))[0]
                                      if (len(matches3) < 1):
                                          print("No spw match found: %d not in %s" % (ispw, str(cal_desc_idBP)))
                                  else:
                                      matches3 = np.where(ispw == np.array(spwBP))[0]
                                      if (len(matches3) < 1):
                                          print("No spw match found: %d not in %s" % (ispw, str(spwBP)))
# #     # #                        print("spw matches: matches3 = ", matches3)

                                  matches12 = np.intersect1d(matches1,matches2)
                                  if (len(matches12) < 1):
                                      print("No match between: %s and %s" % (str(matches1), str(matches2)))
# #     # #                        print("antenna&time matches: matches12 = ", matches12)

                                  matches = np.intersect1d(matches12, matches3)
                                  if (len(matches) < 1):
                                      print("No match between: %s and %s" % (str(matches12), str(matches3)))
# #     # #                        print("antenna&time&spw matches: matches = ", matches)

                                  try:
                                      index = matches[0]  # holds the row number of the matching solution in the BPOLY table
                                  except:
                                      print("No match found for time=%.6f, xant=%d, ispw=%d"  % (uniqueTimes[mytime],xant,ispw))
                                      print("antennasBP = %s" % (str(antennasBP)))
                                      print("cal_desc_idBP = %s" % (str(cal_desc_idBP)))
                                      timesBPstring = "timesBP = "
                                      for i in timesBP:
                                          timesBPstring += "%.6f, " % i
                                      print(timesBPstring)
                                      return()
# #     # #                        print("phase: Using index = %d/%d (mytime=%d), domain=%.3f,%.3f" % (index,len(polynomialPhase),mytime,frequencyLimits[0,index]*1e-9,frequencyLimits[1,index]*1e-9))
                                  if (debug): print("BRowNumber = %d, BPolyRowNumber = %d"  % (BRowNumber, index))
                                  validDomain = [frequencyLimits[0,index], frequencyLimits[1,index]]
                                  cc = calcChebyshev(polynomialPhase[index][0:nPolyPhase[index]], validDomain, frequenciesGHz[index]*1e+9) * 180/math.pi
                                  fa = np.array(frequenciesGHz[index])
                                  if (xfrequencies[0] < xfrequencies[-1]):
                                      matches = np.where(fa>xfrequencies[0])[0]
                                      matches2 = np.where(fa<xfrequencies[-1])[0]
                                  else:
                                      matches = np.where(fa>xfrequencies[-1])[0]
                                      matches2 = np.where(fa<xfrequencies[0])[0]
# #     # #                        print("xfrequencies[0] = %f, xfrequencies[-1] = %f" % (xfrequencies[0], xfrequencies[-1]))
# #     # #                        print("len(matches)=%d, len(matches2)=%d" % (len(matches), len(matches2)))
# #     # #                        print("fa = ", fa)
                                  mymean = complexMeanDeg(np.array(cc)[matches[0]:matches2[-1]+1])
                                  phaseSolutionX = np.mean(gphsx) - mymean + cc

                                  cc = calcChebyshev(polynomialPhase[index][nPolyPhase[index]:2*nPolyPhase[index]], validDomain, frequenciesGHz[index]*1e+9) * 180/math.pi
                                  if (nPolarizations > 1):
                                    if (yfrequencies[0] < yfrequencies[-1]):
                                      matches = np.where(fa>yfrequencies[0])[0]
                                      matches2 = np.where(fa<yfrequencies[-1])[0]
                                    else:
                                      matches = np.where(fa>yfrequencies[-1])[0]
                                      matches2 = np.where(fa<yfrequencies[0])[0]
                                    mymean = complexMeanDeg(np.array(cc)[matches[0]:matches2[-1]+1])
                                    phaseSolutionY = np.mean(gphsy) - mymean + cc
                                  if (bpolyOverlay2):
                                      validDomain = [frequencyLimits2[0,index], frequencyLimits2[1,index]]
                                      cc = calcChebyshev(polynomialPhase2[index][0:nPolyPhase2[index]], validDomain, frequenciesGHz2[index]*1e+9) * 180/math.pi
                                      fa = np.array(frequenciesGHz2[index])
                                      if (xfrequencies[0] < xfrequencies[-1]):
                                          matches = np.where(fa>xfrequencies[0])[0]
                                          matches2 = np.where(fa<xfrequencies[-1])[0]
                                      else:
                                          matches = np.where(fa>xfrequencies[-1])[0]
                                          matches2 = np.where(fa<xfrequencies[0])[0]
                                      mymean = complexMeanDeg(np.array(cc)[matches[0]:matches2[-1]+1])
                                      phaseSolution2X = np.mean(gphsx) + cc - mymean

                                      cc = calcChebyshev(polynomialPhase2[index][nPolyPhase2[index]:2*nPolyPhase2[index]], validDomain, frequenciesGHz2[index]*1e+9) * 180/math.pi
                                      if (yfrequencies[0] < yfrequencies[-1]):
                                          matches = np.where(fa>yfrequencies[0])[0]
                                          matches2 = np.where(fa<yfrequencies[-1])[0]
                                      else:
                                          matches = np.where(fa>yfrequencies[-1])[0]
                                          matches2 = np.where(fa<yfrequencies[0])[0]
                                      mymean = complexMeanDeg(np.array(cc)[matches[0]:matches2[-1]+1])
                                      phaseSolution2Y = np.mean(gphsy) + cc - mymean
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies[p], gphs[p],'%s%s' % (pcolor[p],phasemarkstyle), markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      if (corrTypeToString(corr_type[0]) in polsToPlot):
                                          pb.plot(frequenciesGHz[index],phaseSolutionX,'%s%s'%(x2color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolutionX, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                          pb.plot(frequenciesGHz2[index],phaseSolution2X,'%s%s'%(x3color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                          newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolution2X, sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                      if (nPolarizations == 2):
                                         if (corrTypeToString(corr_type[1]) in polsToPlot):
                                            pb.plot(frequenciesGHz[index],phaseSolutionY,'%s%s'%(y2color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                            newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolutionY, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                            pb.plot(frequenciesGHz2[index],phaseSolution2Y,'%s%s'%(y3color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                            newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolution2Y, sideband,plotrange,xchannels2,chanrangePercent=chanrangePercent)
                                  else:
                                      for p in range(nPolarizations):
                                          if (corrTypeToString(corr_type[p]) in polsToPlot):
                                              pb.plot(pfrequencies[p], gphs[p],'%s%s'%(pcolor[p],phasemarkstyle), markersize=markersize,markeredgewidth=markeredgewidth)
                                              newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      if (corrTypeToString(corr_type[0]) in polsToPlot):
                                         pb.plot(frequenciesGHz[index],phaseSolutionX,'%s%s'%(x2color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                         newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolutionX, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                      if (nPolarizations == 2):
                                         if (corrTypeToString(corr_type[1]) in polsToPlot):
                                            pb.plot(frequenciesGHz[index],phaseSolutionY,'%s%s'%(y2color,bpolymarkstyle),markeredgewidth=markeredgewidth)
                                            newylimits = recalcYlimitsFreq(chanrange, newylimits, phaseSolutionY, sideband,plotrange,xchannels,chanrangePercent=chanrangePercent)
                                  # endif (bpolyOverlay2)
                                  # Adding the following 4 lines on March 14, 2013
                                  (y0,y1) = pb.ylim()
                                  if (y1-y0 < minPhaseRange):
                                      # this must come before defining ticks
                                      SetNewYLimits([-minPhaseRange,minPhaseRange])
                          else:
                              # we are not overlaying any B or polynomial solutions   'phase vs. freq'
                              for p in range(nPolarizations):
                                  if (corrTypeToString(corr_type[p]) in polsToPlot):
                                      if (overlayAntennas or overlayTimes):
                                        pdesc = pb.plot(pfrequencies[p], gphs[p],'%s'%(phasemarkstyles[p]), markersize=markersize,markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband,plotrange,xchannels,chanrangePercent=chanrangePercent) # Apr 2, 2012
                                        if (overlayAntennas and overlayTimes==False):
                                            pb.setp(pdesc, color=overlayColors[xctr])
                                        elif (overlayTimes and overlayAntennas==False):
                                            pb.setp(pdesc, color=overlayColors[mytime])
                                        elif (overlayTimes): # try to support antenna,time
                                            if (myUniqueTime != []):
                                                pb.setp(pdesc, color=overlayColors[myUniqueTime])
                                                # The third 'or' below is needed if pol='0' is flagged on antenna 0. -- 2012/10/12 (original spot)
                                                if (p==0 or len(polsToPlot)==1 or myUniqueColor==[]):
                                                    myUniqueColor.append(overlayColors[len(myUniqueColor)])
                                                pb.setp(pdesc, color=myUniqueColor[-1])
                                      else:
                                        pb.plot(pfrequencies[p], gphs[p],'%s%s'%(pcolor[p],phasemarkstyles[0]), markersize=markersize,markeredgewidth=markeredgewidth)
                                        newylimits = recalcYlimitsFreq(chanrange, newylimits, gphs[p], sideband, plotrange,xchannels,chanrangePercent=chanrangePercent)
                                  if (sum(xflag)>0):
# #     # #                            print("phase frame %d: Resetting xaxis frequency range to counteract flagged data" % (xframe))
                                      myxrange = np.max(frequencies)-np.min(frequencies)
                                      SetNewXLimits([np.min(frequencies)-0.15*myxrange, np.max(frequencies)+0.15*myxrange],17)
                                  if (len(gphs[p]) > 0):
                                      if (np.max(gphs[p]) < minPhaseRange and np.min(gphs[p]) > -minPhaseRange):
                                          SetNewYLimits([-minPhaseRange,minPhaseRange])
                          #endif bOverlay

                          if (1==1):
                              pb.xlabel(xlabelString, size=mysize)
                      #endif xaxis='chan'/freq  for 'phase'
                      if (overlayTimes):
                          timeString =''
                      else:
                          timeString = ',  t%d/%d  %s' % (mytime,nUniqueTimes-1,utstring(uniqueTimes[mytime],3))
                          if (scansForUniqueTimes != []):
                              if (scansForUniqueTimes[mytime]>=0):
                                  timeString = ',  scan%d  %s' % (scansForUniqueTimes[mytime],utstring(uniqueTimes[mytime],3))
                      spwString = buildSpwString(overlaySpws, overlayBasebands,
                                                 spwsToPlot, ispw, originalSpw[ispw],
                                                 observatoryName, baseband,
                                                 showBasebandNumber)
                      titleString = "%sspw%s,  field %d: %s%s" % (antennaString,
                                                                  spwString,uniqueFields[fieldIndex],fieldString,timeString)
                      pb.title(titleString,size=titlesize-int(len(titleString)//(maxCharsBeforeReducingTitleFontSize//subplotCols)))
                      if (abs(plotrange[0]) > 0 or abs(plotrange[1]) > 0):
                          SetNewXLimits([plotrange[0],plotrange[1]],18)

                      # Here is 1st place where we eliminate any white space on the right and left edge of the plots: 'phase'
                      else:
                          if (xaxis.find('chan')>=0):
                              SetNewXLimits([channels[0],channels[-1]])
                          else:
                              if (zoom != 'intersect'):
                                  if (overlaySpws or overlayBasebands):
                                      SetNewXLimits(frequencyRangeToPlotInBaseband[bbctr])
                                  else:
                                      SetNewXLimits([frequencies[0], frequencies[-1]])
                              if (bOverlay):
                                  if (xrange2 > myxrange+0.1 and zoom != 'intersect'):
                                      TDMisSecond = True

                      if (abs(plotrange[2]) > 0 or abs(plotrange[3]) > 0):
                          if (amplitudeWithPhase == False or phase == ''):
                              SetNewYLimits([plotrange[2],plotrange[3]])
                      if (amplitudeWithPhase and phase != ''):
                          if (phase[0] != 0 or phase[1] != 0):
                              SetNewYLimits(phase)


                      (y0,y1) = pb.ylim()
                      if (y1-y0 < minPhaseRange):
                            # this must come before defining ticks
                            SetNewYLimits([-minPhaseRange,minPhaseRange])
                            SetNewYLimits(newylimits)  # added 10/2/2012 for the case of only 1 data point
                      if (amplitudeWithPhase and phase != ''):
                          if (phase[0] != 0 or phase[1] != 0):
                              SetNewYLimits(phase)
                      (y0,y1) = pb.ylim()
                      ResizeFontsSetGrid(adesc,mysize)
                      pb.ylabel(yPhaseLabel, size=mysize)
                      pb.subplots_adjust(hspace=myhspace, wspace=mywspace)
                      ylim = pb.ylim()
                      xlim = pb.xlim()
                      myxrange = xlim[1]-xlim[0]
                      yrange = ylim[1]-ylim[0]
# #     # #            print("phase: ylim, yrange = ",  ylim, yrange)
                      myap = 0
                      if (overlayAntennas == False and overlayTimes == False and bOverlay == False and
                          ((overlaySpws == False and overlayBasebands == False) or spwctr==spwctrFirstToPlot)):
                          # draw polarization labels
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          for p in range(nPolarizations):
                                if (corrTypeToString(corr_type[p]) in polsToPlot):
                                    pb.text(x0, y0-0.03*subplotRows*p, corrTypeToString(corr_type[p]), color=pcolor[p],
                                            size=mysize, transform=pb.gca().transAxes)
                                    if (channeldiff > 0):
                                        pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                                corrTypeToString(corr_type[p])+' MAD = %.4f, St.Dev = %.4f'%(gphs_mad[p]['mad'],gphs_std[p]['std']),
                                                color=pcolor[p],size=mysize, transform=pb.gca().transAxes)
                          if (xframe == firstFrame):
                                # draw title including caltable name
                                caltableList = caltableTitle
                                if (bpolyOverlay):
                                      caltableList += ', ' + caltable2 + ' (degamp=%d, degphase=%d)'%(nPolyAmp[index]-1,nPolyPhase[index]-1)
                                      if (bpolyOverlay2):
                                            caltableList += ', ' + caltable3 + ' (degamp=%d, degphase=%d)'%(nPolyAmp2[index]-1,nPolyPhase2[index]-1)
                                pb.text(xstartTitle, ystartTitle, caltableList, size=titlesize,
                                        color='k', transform=pb.gcf().transFigure)
                      elif (overlayAntennas==True and xant==antennasToPlot[-1] and bOverlay==False  # ):
                            and overlayTimes==False):  # try to support antenna,time   avoid antenna labels 'phase'
                          # We do this last, because by then, the limits will be stable.
                          # draw polarization labels for overlayAntennas
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          if (corrTypeToString(corr_type[0]) in polsToPlot):
                              if (channeldiff > 0):
                                  pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                          corrTypeToString(corr_type[p])+' MAD = %.4f, St.Dev = %.4f'%(gphs_mad[p]['mad'],gphs_std[p]['std']),
                                          color=overlayColors[0], size=mysize, transform=pb.gca().transAxes)
                              if (phasemarkstyle.find('-')>=0):
                                  pb.text(x0, y0-0.03*subplotRows*0, corrTypeToString(corr_type[0])+' solid', color=overlayColors[0],
                                          fontsize=mysize, transform=pb.gca().transAxes)
                              else:
                                  pb.text(x0+0.02, y0-0.03*subplotRows*0, corrTypeToString(corr_type[0]), color=overlayColors[0],
                                          fontsize=mysize, transform=pb.gca().transAxes)
                                  pdesc = pb.plot([x0], [y0+0.015-0*0.03*subplotRows], '%sk'%phasemarkstyle, markersize=markersize,
                                                  scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
                          if (len(corr_type) > 1):
                            if (corrTypeToString(corr_type[1]) in polsToPlot):
                              if (channeldiff > 0):
                                  pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                          corrTypeToString(corr_type[p])+' MAD = %.4f, St.Dev = %.4f'%(gphs_mad[p]['mad'],gphs_std[p]['std']),
                                          color=overlayColors[0], size=mysize, transform=pb.gca().transAxes)
                              if (phasemarkstyle2.find('--')>=0):
                                  pb.text(x0, y0-0.03*subplotRows*1, corrTypeToString(corr_type[1])+' dashed', color=overlayColors[0],
                                          fontsize=mysize, transform=pb.gca().transAxes)
                              else:
                                  pb.text(x0+0.02, y0-0.03*subplotRows*1, corrTypeToString(corr_type[1]), color=overlayColors[0],
                                          fontsize=mysize, transform=pb.gca().transAxes)
                                  pdesc = pb.plot([x0], [y0+0.015*subplotRows-0.03*subplotRows*1],'%sk'%phasemarkstyle2, markersize=markersize,
                                                  scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
                          if (xframe == firstFrame):
                              # draw title including caltable name
                              pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k',
                                      transform=pb.gcf().transFigure)
                              DrawAntennaNames(msAnt, antennasToPlot, msFound, mysize, overlayColors)
                      elif (overlayTimes==True and bOverlay == False
                            and overlayAntennas==False):  # try to support antenna,time
                          doneOverlayTime = True # assumed until proven otherwise in the 'for' loop
                          for f in fieldIndicesToPlot:
                              if (uniqueTimes[mytime] < uniqueTimesPerFieldPerSpw[ispwInCalTable][f][-1]-solutionTimeThresholdSeconds and
                                  uniqueTimes[mytime] < timerangeListTimes[-1]):
                                if tableFormat >= 34:                                           # added 2024Aug
                                  if scansForUniqueTimes[mytime] != scansForUniqueTimes[-1]:    # fix for CAS-14096
                                    doneOverlayTime = False         
                                else:                                                           # added 2024Aug
                                  doneOverlayTime = False                                       # added 2024Aug  
                          if (doneOverlayTime):
                              # either it is the last time of any times in solution, or the last time in the list of times to plot
                              mytime = nUniqueTimes-1
                              # draw polarization labels for overlayTimes
                              # We do this last, because by then, the limits will be broad enough and stable.
                              x0 = xstartPolLabel
                              y0 = ystartPolLabel
                              if (corrTypeToString(corr_type[0]) in polsToPlot):
                                if (channeldiff > 0):
                                    p = 0
                                    pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                            corrTypeToString(corr_type[p])+' MAD = %.4f'%(gphs_mad[p]['mad']),
                                            color='k', size=mysize, transform=pb.gca().transAxes)
                                if (phasemarkstyle.find('-')>=0):
                                    pb.text(x0, y0, corrTypeToString(corr_type[0])+' solid', color='k',
                                            fontsize=mysize, transform=pb.gca().transAxes)
                                else:
                                    pb.text(x0+0.02, y0, corrTypeToString(corr_type[0]), color='k',
                                            fontsize=mysize, transform=pb.gca().transAxes)
                                    pdesc = pb.plot([x0], [y0+0.015*subplotRows], '%sk'%phasemarkstyle, markersize=markersize,
                                                    scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
                              if (len(corr_type) > 1):
                                if (corrTypeToString(corr_type[1]) in polsToPlot):
                                  if (channeldiff > 0):
                                      p = 1
                                      pb.text(x0, ystartMadLabel-0.03*subplotRows*p,
                                              corrTypeToString(corr_type[p])+' MAD = %.4f'%(gphs_mad[p]['mad']),
                                              color='k', size=mysize, transform=pb.gca().transAxes)
                                  if (phasemarkstyle2.find('--')>=0):
                                      pb.text(x0, y0-0.03*subplotRows, corrTypeToString(corr_type[1])+' dashed',
                                              color='k',fontsize=mysize, transform=pb.gca().transAxes)
                                  else:
                                      pb.text(x0+0.02, y0-0.03*subplotRows, corrTypeToString(corr_type[1]),
                                              color='k', fontsize=mysize, transform=pb.gca().transAxes)
                                      pdesc = pb.plot([x0], [y0+0.015*subplotRows-0.03*subplotRows], '%sk'%phasemarkstyle2,
                                                      markersize=markersize, scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
                              if (xframe == firstFrame):
                                  # draw title including caltable name
                                  pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k',
                                          transform=pb.gcf().transFigure)
                                  if (debug): print("drawOverlayTimeLegends loc 4")
                                  drawOverlayTimeLegends(xframe,firstFrame,xstartTitle,ystartTitle,
                                                         caltable,titlesize,fieldIndicesToPlot,
                                                         ispwInCalTable,uniqueTimesPerFieldPerSpw,
                                                         timerangeListTimes, solutionTimeThresholdSeconds,
                                                         debugSloppyMatch,ystartOverlayLegend,debug,mysize,
                                                         fieldsToPlot,myUniqueColor,timeHorizontalSpacing,
                                                         fieldIndex, overlayColors, antennaVerticalSpacing,
                                                         overlayAntennas, timerangeList, caltableTitle,
                                                         mytime, scansToPlotPerSpw[ispw], scansForUniqueTimes,
                                                         uniqueSpwsInCalTable, uniqueTimes)

                      elif (overlayAntennas and overlayTimes):  # Oct 23, 2012
                          # This will only happen for: try to support overlay='antenna,time'
                          if (xframe == firstFrame and mytime==0 and xctr==firstUnflaggedAntennaToPlot and bOverlay==False):
                              # draw title including caltable name
                              pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k',
                                      transform=pb.gcf().transFigure)
                              DrawBottomLegendPageCoords(msName, uniqueTimes[mytime], mysize, figfile)

                      #endif (overlayAntennas == False and overlayTimes == False and bOverlay == False)

                      # Here is 2nd place where we eliminate any white space on the right and left edge of the plots: 'phase'
                      if (abs(plotrange[2]) > 0 or abs(plotrange[3]) > 0):
                          if (amplitudeWithPhase == False or phase == ''):
                              SetNewYLimits([plotrange[2],plotrange[3]])
                      if (phase != '' and amplitudeWithPhase):
                          if (phase[0] != 0 or phase[1] != 0):
                              SetNewYLimits(phase)
                      if (plotrange[0]==0 and plotrange[1]==0):
                          if (xaxis.find('chan')>=0):
                              SetNewXLimits([channels[0],channels[-1]])
                          else:
                              if (zoom != 'intersect'):
                                  if (overlaySpws or overlayBasebands):
                                      SetNewXLimits(frequencyRangeToPlotInBaseband[bbctr])
                                  else:
                                      SetNewXLimits([frequencies[0], frequencies[-1]])
                              if (bOverlay):
                                  if (xrange2 >= myxrange and zoom != 'intersect'):
                                      # This is necessary if caltable2=TDM and caltable=FDM
                                      SetNewXLimits([frequencies2[0], frequencies2[-1]])
                                  if (xrange2 > myxrange+0.1 and zoom != 'intersect'):
                                      TDMisSecond = True
                      else:
                          SetNewXLimits([plotrange[0], plotrange[1]])

                      # I need the following line for chanrange to work
                      if (chanrange[0] != 0 or chanrange[1] != 0):
                          SetLimits(plotrange, chanrange, newylimits, channels, frequencies, pfrequencies,
                                    ampMin, ampMax, xaxis,pxl, chanrangeSetXrange,
                                    chanrangePercent)

                      # Finally, draw the atmosphere and FDM windows, if requested.  'phase'
                      if ((overlayAntennas==False and overlayTimes==False) or
                          (overlayAntennas==True and overlayTimes==False and xant==antennasToPlot[-1]) or
                          (overlayTimes==True and overlayAntennas==False and doneOverlayTime) or
                          (xant==antennasToPlot[-1] and doneOverlayTime)
                          ):
                          if ((showatm or showtsky) and len(atmString)>0):
                              DrawAtmosphere(showatm, showtsky, subplotRows, atmString,
                                             mysize, TebbSky, plotrange, xaxis, atmchan,
                                             atmfreq, transmission, subplotCols,
                                             showatmPoints=showatmPoints, xframe=xframe,
                                             channels=channels, mylineno=lineNumber(),
                                             overlaySpws=overlaySpws,
                                             overlayBasebands=overlayBasebands,
                                             drewAtmosphere=drewAtmosphere,loc=205,
                                             showtsys=showtsys, Trx=Trx)
                              if (LO1):
                                  DrawAtmosphere(showatm,showtsky, subplotRows, atmString,
                                                 mysize, TebbSky, plotrange, xaxis, atmchanImage,
                                                 atmfreqImage, transmissionImage, subplotCols,
                                                 LO1, xframe, firstFrame, showatmPoints,
                                                 channels=channels, mylineno=lineNumber(),
                                                 overlaySpws=overlaySpws,
                                                 overlayBasebands=overlayBasebands,
                                                 drewAtmosphere=drewAtmosphere, loc=206,
                                                 showtsys=showtsys, Trx=Trx)
                              drewAtmosphere = True

                          if (xaxis.find('freq')>=0 and showfdm and nChannels <= 256):
                              if (tableFormat == 33):
                                  showFDM(originalSpw_casa33, chanFreqGHz_casa33, baseband, showBasebandNumber, basebandDict, overlayColors)
                              else:
                                  showFDM(originalSpw, chanFreqGHz, baseband, showBasebandNumber, basebandDict, overlayColors)

                      if (bOverlay):
                          # draw polarization labels for bOverlay
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          for p in range(nPolarizations):
                              if (corrTypeToString(corr_type[p]) in polsToPlot):
                                  pb.text(x0, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p])+'-c1',
                                          color=pcolor[p],size=mysize,transform=pb.gca().transAxes)
                                  pb.text(x0, y0-(p*0.03+0.06)*subplotRows, corrTypeToString(corr_type[p])+'-c2',
                                          color=p2color[p],size=mysize, transform=pb.gca().transAxes)
                      if (bpolyOverlay and xaxis.find('freq')>=0):
                          # draw polarization labels for bpolyOverlay
                          x0 = xstartPolLabel
                          y0 = ystartPolLabel
                          if (xcolor != x2color):
                              for p in range(nPolarizations):
                                  if (corrTypeToString(corr_type[p]) in polsToPlot):
                                      pb.text(x0+0.1, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p]), color=p2color[p],
                                              size=mysize, transform=pb.gca().transAxes)
                          if (bpolyOverlay2):
                              for p in range(nPolarizations):
                                    if (corrTypeToString(corr_type[p]) in polsToPlot):
                                          pb.text(x0+0.2, y0-p*0.03*subplotRows, corrTypeToString(corr_type[p]), color=p3color[p],
                                                  size=mysize, transform=pb.gca().transAxes)

                  # endif (yaxis='phase')

                  redisplay = False

                  if (xframe == lastFrame):
                    if (debug):
                        print("*** mytime+1=%d,  nUniqueTimes=%d, timerangeList[-1]=%d, doneOverlayTime=%s" % (mytime+1, nUniqueTimes,timerangeList[-1],doneOverlayTime))
                        print("*** xant=%d, antennasToPlot[-1]=%d, overlayAntennas=%s, overlayTimes=%s" % (xant,antennasToPlot[-1],overlayAntennas,overlayTimes))
                        print("*** xframe=%d, lastFrame=%d, xctr=%d, spwctr=%d, len(antennasToPlot)=%d, len(spwsToPlot)=%d" % (xframe,lastFrame,xctr,spwctr,len(antennasToPlot), len(spwsToPlot)))
                    myIndexTime = uniqueTimesPerFieldPerSpw[ispwInCalTable][fieldIndex][-1]
                    if (debug):
                        print("myIndexTime = ", myIndexTime)
                    if tableFormat >= 34:                            ### added 2024Aug
                        scansToPlotHere = scansToPlotPerSpw[ispw]    ### added 2024Aug
                    else:                                            ### added 2024Aug
                        scansToPlotHere = scansToPlot                ### added 2024Aug
                    matched,mymatch = sloppyMatch(myIndexTime,uniqueTimes,solutionTimeThresholdSeconds,
                                                  mytime, scansToPlotHere, scansForUniqueTimes,
                                                  whichone=True, myprint=False)
                    if (matched==False and scansForUniqueTimes[mytime] in scansToPlotPerSpw[ispw]):
                        print("---------- 2) Did not find %f within %.1f seconds of anything in %s" % (myIndexTime,solutionTimeThresholdSeconds,str(uniqueTimes)))
                        print("Try re-running with a smaller solutionTimeThresholdSeconds (currently %f)" % (solutionTimeThresholdSeconds))
                        return
                    else:
                        # we are on the final time to be plotted
                        if (debug): print("on the final time")
                        mytimeTest = mytime==nUniqueTimes-1
                    if (debug):
                        print("mytimeTest = %s" % (mytimeTest))
                    if (scansForUniqueTimes == []):
                        # old 3.3 cal tables will land here
                        scanTest = False
                        scanTest2 = False
                    else:
                        if (debug):
                            print("ispw=%d len(scansToPlotPerSpw[ispw])=%d   mytime=%d, len(scansForUniqueTimes)=%d" % (ispw,len(scansToPlotPerSpw[ispw]),mytime,len(scansForUniqueTimes)))
                            print("scansToPlotPerSpw = ", scansToPlotPerSpw)
                        if (len(scansToPlotPerSpw[ispw]) == 0):
                            scanTest = False
                        else:
                            scanTest = scansToPlotPerSpw[ispw][-1]==scansForUniqueTimes[mytime]  and (VisCal != 'SDSKY_PS' or mytime == timerangeList[-1]) ### added second expression 2024Aug27
                        highestSpwIndexInSpwsToPlotThatHasCurrentScan = \
                            computeHighestSpwIndexInSpwsToPlotThatHasCurrentScan(spwsToPlot, scansToPlotPerSpw, scansForUniqueTimes[mytime])
                        if (highestSpwIndexInSpwsToPlotThatHasCurrentScan == -1):
                            scanTest2 = False
                        else:
                            scanTest2 = (spwctr == highestSpwIndexInSpwsToPlotThatHasCurrentScan)
                    if ((overlayAntennas==False and overlayTimes==False and overlaySpws==False and overlayBasebands==False)
                        # either it is the last time of any, or the last time in the list of times to plot
                        or (overlayAntennas==False and overlaySpws==False and overlayBasebands==False and (mytime+1==nUniqueTimes or mytime == timerangeList[-1])) # or mytimeTest)) # removed on July 25,2013
                        or (xant==antennasToPlot[-1] and overlayAntennas==True and overlayTimes==False and overlaySpws==False and overlayBasebands==False)
                        # The following case is needed to prevent frame=225 in test86 (spectral scan dataset with overlay='spw')
                        #   and the lack of showing of 7 of 8 of the spws in final frame of test61.  scanTest2 matches both cases.
                        or (scanTest and scanTest2 and overlaySpws and overlayAntennas==False and overlayTimes==False)
                        or ((spwctr==len(spwsToPlot)-1) and (overlayBasebands or overlaySpws) and overlayAntennas==False and overlayTimes==False)
                        # following case is needed for scans parameter with overlay='time'
                        or (overlayTimes and scanTest and overlayAntennas==False)
                        # Following case is needed to make subplot=11 to work for: try to support overlay='antenna,time' :  'phase'
                        or (xframe == lastFrame and overlayTimes and overlayAntennas and
                            xctr+1==len(antennasToPlot) and
                            mytimeTest and
                            spwctr<len(spwsToPlot))
                        or (doneOverlayTime and overlayTimes==True
                            and overlayAntennas==False
                            )):
                      if (debug):
                          print("entered 'if' block")
                      DrawBottomLegendPageCoords(msName, uniqueTimes[mytime], mysize, figfile)

                      # added len(pages)>0 on July 30, 2013 to prevent crash when called with single
                      # antenna and subplot=11 and all solutions flagged.
                      if (len(figfile) > 0 and len(pages)>0):
                          if (debug):
                              print("calling makeplot")
                          plotfiles.append(makeplot(figfile,msFound,msAnt,
                                                    overlayAntennas,pages,pagectr,
                                                    density,interactive,antennasToPlot,
                                                    spwsToPlot,overlayTimes,overlayBasebands,
                                                    4,xant,ispw,subplot,resample,debug,
                                                    figfileSequential, figfileNumber))
                          if (debug):
                              print("done makeplot")
                          figfileNumber += 1
                      myinput = ''
                      donetime = time.time()
                      drewAtmosphere = False # needed for CAS-7187 (subplot=11)
                      if (interactive):
                          pb.draw()
# #     # #                myinput = raw_input("(%.1f sec) Press return for next screen (b for backwards, q to quit): "%(donetime-mytimestamp))
                          myinput = input("Press return for next page (b for backwards, q to quit): ")
                      else:
                          myinput = ''
                      skippingSpwMessageSent = 0
                      mytimestamp = time.time()
                      if (myinput.find('q') >= 0):
                          mytime = len(uniqueTimes)
                          spwctr = len(spwsToPlot)
                          xctr = len(antennasToPlot)
                          bbctr = len(spwsToPlotInBaseband)
                          break
                      if (debug):
                          print("4)Setting xframe to %d" % (xframeStart))
                      xframe = xframeStart
                      myUniqueColor = []
                      pb.subplots_adjust(hspace=myhspace, wspace=mywspace)
                      if (myinput.find('b') >= 0):
                          if (pagectr > 0):
                              if (debug):
                                  print("Decrementing pagectr from %d to %d" % (pagectr, pagectr-1))
                              pagectr -= 1
                          else:
                              if (debug):
                                  print("Not decrementing pagectr=%d" % (pagectr))
                          redisplay = True
                          #redisplay the current page by setting ctrs back to the value they had at start of that page
                          xctr = pages[pagectr][PAGE_ANT]
                          spwctr = pages[pagectr][PAGE_SPW]
                          mytime = pages[pagectr][PAGE_TIME]
                          myap = pages[pagectr][PAGE_AP]
                          xant = antennasToPlot[xctr]
                          antstring, Antstring = buildAntString(xant,msFound,msAnt)
                          ispw = spwsToPlot[spwctr]
# #     # #                print("Returning to [%d,%d,%d,%d]" % (xctr,spwctr,mytime,myap))
                      else:
                          pagectr += 1
                          if (pagectr >= len(pages)):
                              newpage = 1
                          else:
                              newpage = 0
                      if tableFormat >= 34:                         ### added 2024Aug
                          scansToPlotHere = scansToPlotPerSpw[ispw] ### added 2024Aug
                      else:                                         ### added 2024Aug
                          scansToPlotHere = scansToPlot             ### added 2024Aug
                      if (overlayTimes==True and
                          sloppyMatch(uniqueTimesPerFieldPerSpw[ispwInCalTable][fieldIndex][-1],
                                      uniqueTimes[mytime],solutionTimeThresholdSeconds,
                                      mytime, scansToPlotHere, scansForUniqueTimes, # au version     ### modified 2024Aug
                                      myprint=debugSloppyMatch)):
                          # be sure to avoid any more loops through mytime which will cause 'b' button to fail
                          if VisCal != 'SDSKY_PS':
                              mytime = nUniqueTimes
                    else:
                        if (debug):
                            print(">>>>>>>>>>> Not going to new page, uniqueTimes[mytime]=%.8f, uniqueTimesPerFieldPerSpw[ispwInCalTable=%d][fieldIndex=%d][-1]=%.8f" % (uniqueTimes[mytime], ispwInCalTable, fieldIndex, uniqueTimesPerFieldPerSpw[ispwInCalTable][fieldIndex][-1]))
                            print("spwctr=%d ?== (len(spwsToPlot)-1)=%d, spwsToPlot=" % (spwctr,len(spwsToPlot)-1),spwsToPlot)
                            print("test1: %s" % (overlayAntennas==False and overlayTimes==False and overlaySpws==False and overlayBasebands==False))
                            print("test2: %s" % (overlayAntennas==False and overlaySpws==False and overlayBasebands==False and (mytime+1==nUniqueTimes or mytime == timerangeList[-1])))
                            print("test3: %s" % (xant==antennasToPlot[-1] and overlayAntennas==True and overlayTimes==False and overlaySpws==False and overlayBasebands==False))
                            print("*test4: %s" % ((spwctr==len(spwsToPlot)-1) and (overlaySpws or overlayBasebands) and overlayAntennas==False and overlayTimes==False) )
                            print("    * = overlaySpws==True" )
                            print("test5: %s" % (overlayTimes and scanTest))
                            print("test6: %s" % (xframe == lastFrame and overlayTimes and overlayAntennas and xctr+1==len(antennasToPlot) and mytimeTest and spwctr<len(spwsToPlot)))
                            print("test7: %s" % (doneOverlayTime and overlayTimes==True and overlayAntennas==False))

                  if (redisplay == False):
                      if ((overlayAntennas and xctr+1 >= len(antennasToPlot)) or
                          ((overlaySpws or overlayBasebands) and spwctr+1 >= len(spwsToPlot)) or
                          (overlayAntennas==False and overlaySpws==False and overlayBasebands==False)):
                          mytime += 1
                          if (debug):
                              print("AT BOTTOM OF LOOP: Incrementing mytime to %d, setting firstUnflaggedAntennaToPlot to 0" % (mytime))
                          firstUnflaggedAntennaToPlot = 0  # try this
                          doneOverlayTime = False  # added on 08-nov-2012
                          if (overlayBasebands and (uniqueScanNumbers == sorted(scansToPlot))):
                              if (debug): print("Breaking because scans not specified")
                              break
                # end of enormous while(mytime) loop  endwhile mytime
                if (redisplay == False):
                    spwctr += 1
                    if (debug):
                        print(    "---------------------------------------- Incrementing spwctr to %d, spwsToPlot=" % (spwctr), spwsToPlot)
                        if (spwctr < len(spwsToPlot)):
                            print("---------------------------------------- ispw = %d" % (spwsToPlot[spwctr]))
                        else:
                            print("---------------------------------------- done the spws in this baseband (%d)" % (baseband))
                else:
                    if (debug):
                        print("redisplay = True")
       # end of while(spwctr) loop
       if (debug): print("at bottom of spwctr loop, spwctr=%d, incrementing bbctr from %d to %d" % (spwctr,bbctr,bbctr+1))
       bbctr += 1
      # end of while(bbctr) loop
      if (debug): print("at bottom of bbctr loop")
      if (xant >= antennasToPlot[-1] and xframe != xframeStart):
          # this is the last antenna, so make a final plot
          if (len(figfile) > 0):
              plotfiles.append(makeplot(figfile,msFound,msAnt,overlayAntennas,
                                        pages,pagectr,density,interactive,
                                        antennasToPlot,spwsToPlot,overlayTimes,overlayBasebands,
                                        5,xant,ispw,subplot,resample,debug,
                                        figfileSequential,figfileNumber))
              figfileNumber += 1
      if (redisplay == False):
          xctr += 1
          if (debug):
              print("Incrementing xctr to %d" % (xctr))
      if (overlayAntennas):
          if (debug):
              print("Breaking out of antenna loop because we are done -------------------")
          break
    # end of while(xant) loop
    if (debug): print("Finished while(xant) loop----------------")
    pb.draw()
    if (len(plotfiles) == 1 and figfileSequential):
        # rename the single file to remove ".000"
        newplotfiles = [plotfiles[0].split('.000.png')[0]+'.png']
        print("renaming %s to %s" % (plotfiles[0],newplotfiles[0]))
        os.system('mv %s %s' % (plotfiles[0],newplotfiles[0]))
        plotfiles = newplotfiles
    if (len(plotfiles) > 0 and buildpdf):
      pdfname = figfile+'.pdf'
      filelist = ''
      plotfiles = np.unique(plotfiles)
      for i in range(len(plotfiles)):
        cmd = '%s -density %d %s %s.pdf' % (convert,density,plotfiles[i],plotfiles[i].split('.png')[0])
        casalogPost(debug,"Running command = %s" % (cmd))
        mystatus = os.system(cmd)
        if (mystatus != 0):
            print("ImageMagick's convert command not found, no PDF built")
            buildpdf = False
            break
        filelist += plotfiles[i].split('.png')[0] + '.pdf '
      if (buildpdf and (len(plotfiles)>1 or not figfileSequential)):
          # The following 2 lines reduce the total number of characters on the command line, which
          # was apparently a problem at JAO for Liza.
          filelist = ' '.join(pruneFilelist(filelist.split()))
          pdfname = pruneFilelist([pdfname])[0]
          cmd = '%s %s cat output %s' % (pdftk, filelist, pdfname)
          casalogPost(debug,"Running command = %s" % (cmd))
          mystatus = os.system(cmd)
          if (mystatus != 0):
              cmd = '%s -q -sPAPERSIZE=letter -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=%s %s' % (gs,pdfname,filelist)
              casalogPost(debug,"Running command = %s" % (cmd))
              mystatus = os.system(cmd)
          if (mystatus == 0):
              casalogPost(debug,"PDF left in %s" % (pdfname))
              print("PDF left in %s" % (pdfname))
              os.system("rm -f %s" % filelist)
          else:
              print("Both pdftk and ghostscript are missing, so no PDF built.")

    showFinalMessage(overlayAntennas, solutionTimeSpread, nUniqueTimes)

    if (channeldiff>0):
        # Compute median over all antennas, or at least those completed before 'q' was hit
        madstats['median'] = dict.fromkeys(spwsToPlot)
        spwvalue = {}
        spwvalue['amp'] = []
        spwvalue['phase'] = []
        for j in spwsToPlot:
            madstats['median'][j] = dict.fromkeys(timerangeList) # dict.fromkeys(range(len(uniqueTimes)))
            for k in timerangeList: # range(len(uniqueTimes)):
                madstats['median'][j][k] = dict.fromkeys(list(range(nPolarizations)))
                for l in range(nPolarizations):
                    if (yaxis == 'both'):
                        madstats['median'][j][k][l] = {'amp': None, 'phase': None}
                    elif (yaxis == 'phase'):
                        madstats['median'][j][k][l] = {'phase': None}
                    else:
                        # this includes tsys and amp
                        madstats['median'][j][k][l] = {'amp': None}
                    for m in madstats['median'][j][k][l].keys():
                        value = []
                        for i in madstats.keys():  # loop over antennas
                            if (i != 'median' and i != 'platforming'):
                                if (madstats[i][j][k][l][m] != None):
                                    if (debug): print(("madstats[%s][%d][%d][%d][%s] = " % (i,j,k,l,m), madstats[i][j][k][l][m]))
                                    value.append(madstats[i][j][k][l][m])
                                    spwvalue[m].append(madstats[i][j][k][l][m])
                        madstats['median'][j][k][l][m] = np.median(value)
        # now add another spw which is the median over spw,time,polarization
        if (yaxis == 'both'):
            madstats['median']['median']={'amp': np.median(spwvalue['amp']),
                                          'phase': np.median(spwvalue['phase'])}
        elif (yaxis == 'phase'):
            madstats['median'][j][k][l] = {'phase': np.median(spwvalue['phase'])}
        else:
            madstats['median']['median'] = {'amp': np.median(spwvalue['amp'])}

        mymsmd.close()
        return(madstats)
    else:
        if (msFound and mymsmd != ''):
            mymsmd.close()
        return()
    # end of plotbandpass

def GetFieldIdsForFieldName(token, mymsmd, msFields):
    if (mymsmd != '' and mymsmd != None):
        return(mymsmd.fieldsforname(token)[0])
    else:
        return(list(msFields).index(token))

def GetFieldNamesForFieldId(u, mymsmd, msFields):
    if (mymsmd != '' and mymsmd != None):
        return(mymsmd.namesforfields(u)[0])
    else:
        print("B")
        return(msFields[u])

def DrawAntennaNamesForOverlayAntennas(xstartPolLabel, ystartPolLabel, polsToPlot, corr_type, channeldiff, ystartMadLabel, subplotRows, gamp_mad, gamp_std, overlayColors, mysize, ampmarkstyle, markersize, markeredgewidth, msAnt, msFound, antennasToPlot, ampmarkstyle2, xframe, firstFrame, caltableTitle, titlesize, debug=False):
    if (debug): print("overlayAntennas=True")
    x0 = xstartPolLabel
    y0 = ystartPolLabel
    # draw polarization labels
    if (debug): print("1) overlayAntennas=True")
    if (corrTypeToString(corr_type[0]) in polsToPlot):
      if (channeldiff > 0):
          pb.text(x0, ystartMadLabel-0.03*subplotRows*0,
                  corrTypeToString(corr_type[0])+' MAD = %.4f, St.Dev = %.4f'%(gamp_mad[0]['mad'],gamp_std[0]['std']),
                  color=overlayColors[0],size=mysize, transform=pb.gca().transAxes)
      if (ampmarkstyle.find('-')>=0):
          pb.text(x0, y0, corrTypeToString(corr_type[0])+' solid', color=overlayColors[0],size=mysize,
                  transform=pb.gca().transAxes)
      else:
          pb.text(x0+0.02, y0, corrTypeToString(corr_type[0]), color=overlayColors[0],size=mysize,
                  transform=pb.gca().transAxes)
          pdesc = pb.plot([x0-0.01], [y0], '%sk'%ampmarkstyle, markersize=markersize,
                          scalex=False,scaley=False, transform=pb.gca().transAxes,markeredgewidth=markeredgewidth)
    if (debug): print("2) overlayAntennas=True")
    if (len(corr_type) > 1):
     if (corrTypeToString(corr_type[1]) in polsToPlot):
      if (channeldiff > 0):
          pb.text(x0, ystartMadLabel-0.03*subplotRows*1,
                  corrTypeToString(corr_type[1])+' MAD = %.4f, St.Dev = %.4f'%(gamp_mad[1]['mad'],gamp_std[1]['std']),
                  color=overlayColors[0],size=mysize, transform=pb.gca().transAxes)
      if (ampmarkstyle2.find('--')>=0):
        pb.text(x0, y0-0.03*subplotRows, corrTypeToString(corr_type[1])+' dashed',
                color=overlayColors[0],size=mysize, transform=pb.gca().transAxes)
      else:
        pb.text(x0+0.02, y0-0.03*subplotRows, corrTypeToString(corr_type[1]),
                color=overlayColors[0],size=mysize, transform=pb.gca().transAxes)
        pdesc = pb.plot([x0-0.01], [y0-0.03*subplotRows], '%sk'%ampmarkstyle2,
                        markersize=markersize, scalex=False,scaley=False,markeredgewidth=markeredgewidth)
    if (debug): print("3) overlayAntennas=True")
    if (xframe == firstFrame):
        # draw title including caltable name
        if (debug): print("4) overlayAntennas=True")
        pb.text(xstartTitle, ystartTitle, caltableTitle, size=titlesize, color='k',
                transform=pb.gcf().transFigure)
        if (debug): print("5) overlayAntennas=True")
        DrawAntennaNames(msAnt, antennasToPlot, msFound, mysize, overlayColors)
        if (debug): print("6) overlayAntennas=True")

def getTelescopeNameFromCaltable(caltable):
    mytb = table()
    mytb.open(caltable)
    if ('OBSERVATION' in mytb.getkeywords()):
        observationTable = mytb.getkeyword('OBSERVATION').split()[1]
    else:
        observationTable = None
    mytb.close()
    if (observationTable == None):
        return('')
    else:
        return(getTelescopeNameFromCaltableObservationTable(observationTable))


def getTelescopeNameFromCaltableObservationTable(observationTable):
    mytb = table()
    mytb.open(observationTable)
    telescope = mytb.getcell('TELESCOPE_NAME')
    mytb.close()
    return(telescope)

def getCorrTypeByAntennaName(firstAntenna):
    """
    This function is used only if the OBSERVATION table of the caltable is blank and the MS is unavailable.
    """
    print("Using antenna name (%s) to set the polarization type." % (firstAntenna))
    if (firstAntenna.find('ea') >= 0):
        corr_type_string = ['RR','LL']
        corr_type = [5,8]
    elif (firstAntenna.find('dv') >= 0 or firstAntenna.find('da') >= 0 or
          firstAntenna.find('pm') >= 0 or firstAntenna.find('da') >= 0):
        corr_type_string = ['XX','YY']
        corr_type = [9,12]
    else: # SMA
        corr_type_string = ['XX']
        corr_type = [9]
    return(corr_type, corr_type_string, len(corr_type))

def MAD(a, c=0.6745, axis=0):
    """
    Median Absolute Deviation along given axis of an array:

    median(abs(a - median(a))) / c

    c = 0.6745 is the constant to convert from MAD to std; it is used by
    default
    """
    a = np.array(a)
    good = (a==a)
    a = np.asarray(a, np.float64)
    if a.ndim == 1:
        d = np.median(a[good])
        m = np.median(np.fabs(a[good] - d) / c)
    else:
        d = np.median(a[good], axis=axis)
        # I don't want the array to change so I have to copy it?
        if axis > 0:
            aswp = swapaxes(a[good],0,axis)
        else:
            aswp = a[good]
        m = np.median(np.fabs(aswp - d) / c, axis=0)

    return m

def showFinalMessage(overlayAntennas, solutionTimeSpread, nUniqueTimes):
  if (overlayAntennas and solutionTimeSpread > 0 and nUniqueTimes==1):
      print("If not all spws were shown, then try setting solutionTimeThreshold=%.0f seconds" % (solutionTimeSpread+1))

def computeOriginalSpwsToPlot(spwsToPlot, originalSpws, tableFormat, debug):
    if (tableFormat > 33):
        # New caltables use the same numbering as the original ms
        return(spwsToPlot)
    else:
        originalSpwsToPlot = []
        for spw in spwsToPlot:
            originalSpwsToPlot.append(originalSpws[spw])
        return(list(originalSpwsToPlot))

def computeScansForUniqueTimes(uniqueTimes, cal_scans, times, unique_cal_scans,
                               debug=False):
    scansForUniqueTimes = []
    nUniqueTimes = len(uniqueTimes)
    for uT in uniqueTimes:
        if (debug): print("Checking uniqueTime = %s" % (str(uT)))
        scansForUniqueTimes.append(cal_scans[list(times).index(uT)])
    if (len(unique_cal_scans) == 1):
        if (unique_cal_scans[0] != -1):
            if (len(scansForUniqueTimes) != len(np.unique(scansForUniqueTimes))):
                if debug:
                    print("Because there are multiple timestamps per scan, I will not assume there is a one-to-one match.")
            else:
                nUniqueTimes = len(np.unique(scansForUniqueTimes))
        else:
            # This 3.4 table does not have the scan numbers populated
            scansForUniqueTimes = []
            if debug:
                print("Because the scan numbers are either not filled in this table, or the solutions span multiple scans, I will use timestamps instead.")
    else:
        if (len(scansForUniqueTimes) != len(np.unique(scansForUniqueTimes))):
            if debug:
                print("Because there are multiple timestamps per scan, I will not assume there is a one-to-one match.")
        else:
            nUniqueTimes = len(np.unique(scansForUniqueTimes))
    return(scansForUniqueTimes, nUniqueTimes)


def calcChebyshev(coeff, validDomain, x):
    """
    Given a set of coefficients,
    this method evaluates a Chebyshev approximation.
    """
    if (type(x) == float or type(x) == int):
         x = [x]
    myxrange = validDomain[1] - validDomain[0]
    x = -1 + 2*(x-validDomain[0])/myxrange
    coeff[0] = 0
    if (True):
        try:
            # python 2.7
            v = np.polynomial.chebyshev.chebval(x,coeff)
        except:
            # python 2.6
            v = np.polynomial.chebval(x,coeff)
    else:
      # manual approach, before I found chebval()
      v = np.zeros(len(x))
      if (len(coeff) > 0):
          v += coeff[0] * 1
      if (len(coeff) > 1):
          v += coeff[1] * (x)
      if (len(coeff) > 2):
          v += coeff[2] * (2*x**2 - 1)
      if (len(coeff) > 3):
          v += coeff[3] * (4*x**3 - 3*x)
      if (len(coeff) > 4):
          v += coeff[4] * (8*x**4 - 8*x**2 + 1)
      if (len(coeff) > 5):
          v += coeff[5] * (16*x**5 - 20*x**3 + 5*x)
      if (len(coeff) > 6):
          v += coeff[6] * (32*x**6 - 48*x**4 + 18*x**2 - 1)
      if (len(coeff) > 7):
          v += coeff[7] * (64*x**7 -112*x**5 + 56*x**3 - 7*x)
      if (len(coeff) > 8):
          v += coeff[8] * (128*x**8 -256*x**6 +160*x**5 - 32*x**2 + 1)
      if (len(coeff) > 9):
          v += coeff[9] * (256*x**9 -576*x**7 +432*x**5 - 120*x**3 + 9*x)
      if (len(coeff) > 10):
          print("Chebyshev polynomials with degree > 10 are not implemented")

    return(v)

def ResizeFontsSetGrid(adesc,fontsize):
#    print("Called ResizeFontsSetGrid()")
    yFormat = ScalarFormatter(useOffset=False)
    if adesc:
        adesc.yaxis.set_major_formatter(yFormat)
        adesc.xaxis.set_major_formatter(yFormat)
        pb.setp(adesc.get_xticklabels(), fontsize=fontsize)
        pb.setp(adesc.get_yticklabels(), fontsize=fontsize)
        adesc.xaxis.grid(True,which='major')
        adesc.yaxis.grid(True,which='major')

def complexMeanRad(phases):
    # convert back to real and imaginary, take mean, then convert back to phase
    meanSin = np.mean(np.sin(phases))
    meanCos = np.mean(np.cos(phases))
    return(180*np.arctan2(meanSin, meanCos)/math.pi)

def complexMeanDeg(phases):
    # convert back to real and imaginary, take mean, then convert back to phase
    phases *= math.pi/180
    meanSin = np.mean(np.sin(phases))
    meanCos = np.mean(np.cos(phases))
    return(180*np.arctan2(meanSin, meanCos)/math.pi)

def CalcAtmTransmission(chans,freqs,xaxis,pwv,vm, mymsmd,vis,asdm,antenna,timestamp,
                        interval,field,refFreqInTable, net_sideband=0,
                        mytime=0, missingCalWVRErrorPrinted=False, caltable='',
                        Trx=None, showtsys=False, verbose=False):
    """
    chans: all channels, regardless of whether they are flagged
    freqs: frequencies corresponding to chans
    xaxis: what we are plotting on the xaxis: 'chan' or 'freq'
    """
#    print("CalcAtm, field = ", field)
#    print("interval = ", interval)
#    print("refFreqInTable = ", refFreqInTable)
    if (type(mymsmd) == str):
        telescopeName = getTelescopeNameFromCaltable(caltable)
    else:
        telescopeName = mymsmd.observatorynames()[0]
    if (telescopeName.find('ALMA') >= 0):
        defaultPWV = 1.0   # a reasonable value for ALMA in case it cannot be found
    elif (telescopeName.find('VLA') >= 0):
        defaultPWV = 5.0
    else:
        defaultPWV = 5.0
    if (type(pwv) == str):
      if (pwv.find('auto')>=0):
        if (os.path.exists(vis+'/ASDM_CALWVR') or os.path.exists(vis+'/ASDM_CALATMOSPHERE') or
            os.path.exists('CalWVR.xml')):
              if (verbose):
                  print("*** Computing atmospheric transmission using measured PWV, field %d, time %d (%f). ***" % (field,mytime,timestamp))
              timerange = [timestamp-interval/2, timestamp+interval/2]
              if (os.path.exists(vis+'/ASDM_CALWVR') or os.path.exists(vis+'/ASDM_CALATMOSPHERE')):
                  [pwvmean, pwvstd]  = getMedianPWV(vis,timerange,asdm,verbose=False)
              else:
                  [pwvmean, pwvstd]  = getMedianPWV('.',timerange,asdm='',verbose=False)
              if (verbose):
                  print("retrieved pwvmean = %f" % pwvmean)
              retrievedPWV = pwvmean
              if (pwvmean < 0.00001):
                  pwvmean = defaultPWV
        else:
              pwvmean = defaultPWV
              if (missingCalWVRErrorPrinted == False):
                  missingCalWVRErrorPrinted = True
                  if (telescopeName.find('ALMA')>=0):
                      print("No ASDM_CALWVR, ASDM_CALATMOSPHERE, or CalWVR.xml table found.  Using PWV %.1fmm." % pwvmean)
                  else:
                      print("This telescope has no WVR to provide a PWV measurement. Using PWV %.1fmm." % pwvmean)
      else:
          try:
              pwvmean = float(pwv)
          except:
              pwvmean = defaultPWV
    else:
          try:
              pwvmean = float(pwv)
          except:
              pwvmean = defaultPWV

    if (verbose):
        print("Using PWV = %.2f mm" % pwvmean)

    # default values in case we can't find them below
    myqa = quanta()
    airmass = 1.5
    P = 563.0
    H = 20.0
    T = 273.0
    roundedScanTimes = []
    if (type(mymsmd) != str):
        if (verbose):
            print("Looking for scans for field integer = %d" % (field))
        scans = mymsmd.scansforfield(field)
        if (verbose):
            print(("For field %s, Got scans = " % str(field),scans))
        for myscan in scans:
            roundedScanTimes.append(np.unique(np.round(mymsmd.timesforscan(myscan))))
#   This method was much slower and not necessary.  Removed for CAS-8065
#        scantimes = mymsmd.timesforscans(scans) # is often longer than the scans array
#        roundedScanTimes = np.unique(np.round(scantimes,0))
#        scans, roundedScanTimes = getScansForTimes(mymsmd,roundedScanTimes) # be sure that each scantime has a scan associated, round to nearest second to save time (esp. for single dish data)
#        if (verbose): print("scantimes = %s" % (str(scantimes)))
#        if (verbose): print("scans = %s" % (str(scans)))
        mindiff = 1e20
        bestscan = 1
        for i in range(len(roundedScanTimes)):
            stime = roundedScanTimes[i]
            meantime = np.mean(stime)
            tdiff = np.abs(meantime-timestamp)
    #        if (verbose): print("tdiff = %s" % (str(tdiff)))
            if (tdiff < mindiff):
                bestscan = scans[i]
                if (verbose): print("bestscan = %s" % (str(bestscan)))
                mindiff = tdiff
        if (verbose):
              print("For timestamp=%.1f, got closest scan = %d, %.0f sec away" %(timestamp, bestscan,mindiff))
        if (verbose): print("Calling getWeather()")
        [conditions,myTimes] = getWeather(vis,bestscan,antenna,verbose,mymsmd)
        if (verbose): print("Done getWeather()")

        # convert pressure with unit to the value in mbar
        if (verbose): print("conditions = ", conditions)
        P = myqa.convert(myqa.quantity(conditions['pressure'], conditions['pressure_unit']), 'mbar')['value']
        if P < 100:                        ### added 2024Aug
            # then the units are wrong, as in early ALMA data had units="Pa" but value was in mbar
            P = conditions['pressure']     ### added 2024Aug
            casalogPost(verbose,"Ignoring pressure units '%s' because the value implied would be less than 100 mbar."%(conditions['pressure_unit']))         ### added 2024Aug
        H = conditions['humidity']
        T = conditions['temperature']+273.15
        if (P <= 0.0):
            P = 563
        if (H <= 0.0):
            H = 20
    else:
        conditions = {}
    if (type(mymsmd) != str):
      if ('elevation' not in list(conditions.keys())):
        # Someone cleared the POINTING table, so calculate elevation from Ra/Dec/MJD
#        myfieldId =  mymsmd.fieldsforname(mymsmd.fieldsforscan(bestscan))
        myfieldId =  mymsmd.fieldsforscan(bestscan)[0]
        myscantime = np.mean(mymsmd.timesforscan(bestscan))
        mydirection = getRADecForField(vis, myfieldId, verbose)
        if (verbose):
            print("myfieldId = %s" % (str(myfieldId)))
            print("mydirection = %s" % (str(mydirection)))
            print("Scan =  %d, time = %.1f,  Field = %d, direction = %s" % (bestscan, myscantime, myfieldId, str(mydirection)))
        telescopeName = mymsmd.observatorynames()[0]
        if (len(telescopeName) < 1):
            telescopeName = 'ALMA'
        if verbose:                                  ### added 2024Aug to prevent lots of spam to the terminal
            print("telescope = %s" % (telescopeName))
        myazel = computeAzElFromRADecMJD(mydirection, myscantime/86400., telescopeName)
        conditions['elevation'] = myazel[1] * 180/math.pi
        conditions['azimuth'] = myazel[0] * 180/math.pi
        if (verbose):
            print("Computed elevation = %.1f deg" % (conditions['elevation']))
    else:
        conditions['elevation'] = 45
        casalogPost(verbose,"Using 45 deg elevation since the actual value is unavailable.")
        bestscan = -1
    if (verbose):
          print("CalcAtm: found elevation=%f (airmass=%.3f) for scan: %s" % (conditions['elevation'],1/np.sin(conditions['elevation']*np.pi/180.), str(bestscan)))
          print("P,H,T = %f,%f,%f" % (P,H,T))
    if (conditions['elevation'] <= 3):
        print("Using 45 deg elevation instead")
        airmass = 1.0/math.cos(45*math.pi/180.)
    else:
        airmass = 1.0/math.cos((90-conditions['elevation'])*math.pi/180.)

    # get the elevation of the antenna
    geodetic_elevation = 5059
    if os.path.exists(os.path.join(vis, 'ANTENNA')):
        with sdutil.table_manager(os.path.join(vis, 'ANTENNA')) as tb:
            _X, _Y, _Z = (float(i) for i in tb.getcell('POSITION', antenna))
            geodetic_elevation = simutil.simutil().xyz2long(_X, _Y, _Z, 'WGS84')[2]
        if verbose:
            casalogPost(True,"computed geodetic_elevation from ANTENNA table as %s" % str(geodetic_elevation))
            
    tropical = 1
    midLatitudeSummer = 2
    midLatitudeWinter = 3
    numchan = len(freqs)
    # Set the reference freq to be the middle of the middle two channels
    reffreq=0.5*(freqs[numchan//2-1]+freqs[numchan//2])
    originalnumchan = numchan
    while (numchan > MAX_ATM_CALC_CHANNELS):
        numchan //= 2
#        print("Reducing numchan to ", numchan)
        chans = list(range(0,originalnumchan,(originalnumchan//numchan)))

    chansep = (freqs[-1]-freqs[0])/(numchan-1)
    nbands = 1
    if (verbose): print("Opening casac.atmosphere()")
    myat = atmosphere()
    if (verbose): print("Opened")
    fCenter = myqa.quantity(reffreq,'GHz')
    fResolution = myqa.quantity(chansep,'GHz')
    fWidth = myqa.quantity(numchan*chansep,'GHz')
    h0 = 1.0 # km
    dP = 5.0 # mbar
    dPm = 1.1 # unitless ratio
#    print(H, T, geodetic_elevation,P, midLatitudeWinter, maxAltitude, h0, dP, dPm)
    myat.initAtmProfile(humidity=H, temperature=myqa.quantity(T, "K"),
                        altitude=myqa.quantity(geodetic_elevation, "m"),
                        pressure=myqa.quantity(P, 'mbar'),
                        atmType=midLatitudeWinter,
                        h0=myqa.quantity(h0,"km"),
                        maxAltitude=myqa.quantity(maxAltitude,"km"),
                        dP=myqa.quantity(dP,"mbar"),
                        dPm=dPm)
    myat.initSpectralWindow(nbands,fCenter,fWidth,fResolution)
    myat.setUserWH2O(myqa.quantity(pwvmean,'mm'))

#    myat.setAirMass()  # This does not affect the opacity, but it does effect TebbSky, so do it manually.

    n = myat.getNumChan()
    if (verbose): print("numchan = %s" % (str(n)))
    if ctsys.compare_version('<',[4,0,0]):
        dry = np.array(myat.getDryOpacitySpec(0)['dryOpacity'])
        wet = np.array(myat.getWetOpacitySpec(0)['wetOpacity'].value)
        TebbSky = []
        for chan in range(n):  # do NOT use numchan here, use n
            TebbSky.append(myat.getTebbSky(nc=chan, spwid=0).value)
        TebbSky = np.array(TebbSky)
        # readback the values to be sure they got set
        #rf = myat.getRefFreq().value
        #cs = myat.getChanSep().value
    else:   # casa >=4.0
        dry = np.array(myat.getDryOpacitySpec(0)[1])
        wet = np.array(myat.getWetOpacitySpec(0)[1]['value'])
        TebbSky = myat.getTebbSkySpec(spwid=0)[1]['value']
        # readback the values to be sure they got set
        #rf = myqa.convert(myat.getRefFreq(),'GHz')['value']
        #cs = myqa.convert(myat.getChanSep(),'GHz')['value']

    transmission = np.exp(-airmass*(wet+dry))
    TebbSky *= (1-np.exp(-airmass*(wet+dry)))/(1-np.exp(-wet-dry))

    if (refFreqInTable*1e-9>np.mean(freqs)):
        if ((net_sideband % 2) == 0):
            sense = 1
        else:
            sense = 2
    else:
        if ((net_sideband % 2) == 0):
            sense = 2
        else:
            sense = 1

    if (sense == 1):
        # The following looks right for LSB   sense=1
        if (xaxis.find('chan')>=0):
            trans = np.zeros(len(transmission))
            Tebb = np.zeros(len(TebbSky))
            for i in range(len(transmission)):
                trans[i] = transmission[len(transmission)-1-i]
                Tebb[i] = TebbSky[len(TebbSky)-1-i]
            transmission = trans
            TebbSky = Tebb
    if showtsys:
        if Trx is None or Trx == 'auto':
            Trx = au.receiverTrxSpec(au.getBand(freqs[0]*1e9))
        Tsys = (Feff*TebbSky + (1.0-Feff)*Tamb + Trx) * ((1.0 + (1.0-SBGain)) / (Feff*np.exp(-airmass*(wet+dry))))
    else:
        Tsys = None

    # Be sure that number of frequencies matched number of transmission values - CAS-10123
    numchan = len(transmission)
    chans = list(range(len(transmission)))
    # Note that getChanFreq returns units of GHz, but use convert to be sure.
    startFreq = myqa.convert(myat.getChanFreq(0),'GHz')['value']
    endFreq = myqa.convert(myat.getChanFreq(numchan-1),'GHz')['value']
    # print("startFreq=%f  endFreq=%f " % (startFreq, endFreq))
    freq = np.linspace(startFreq, endFreq, numchan)
# old method that fails on spws with an even number of channels, i.e. when the integer refchan
# is half a channel from the center of the span
#    if sense == 2:
#        freq = np.linspace(rf-((numchan-1)/2.)*chansepGHz, rf+((numchan-1)/2.)*chansepGHz, numchan)
#    else:
#        freq = np.linspace(rf+((numchan-1)/2.)*chansepGHz,
#                           rf-((numchan-1)/2.)*chansepGHz, numchan)
    # Therewas a 1-channel offset in CASA 5.0.x (CAS-10228), but it was fixed.
#    if (ctsys.compare_version('<',[5,1,0])):
#        freq += chansepGHz

    if (verbose): print("Done CalcAtmTransmission")
    return(freq, chans, transmission, pwvmean, airmass, TebbSky, missingCalWVRErrorPrinted)

def RescaleTrans(trans, lim, subplotRows, lo1='', xframe=0):
    # Input: the array of transmission or TebbSky values and current limits
    # Returns: arrays of the rescaled transmission values and the zero point
    #          values in units of the frame, and in amplitude.
    debug = False
    yrange = lim[1]-lim[0]
    if (lo1 == ''):
        labelgap = 0.6 # Use this fraction of the margin for the PWV ATM label
    else:
        labelgap = 0.5 # Use this fraction of the margin to separate the top
                       # curve from the upper y-axis
    y2 = lim[1] - labelgap*yrange*TOP_MARGIN/(1.0+TOP_MARGIN)
    y1 = lim[1] - yrange*TOP_MARGIN/(1.0+TOP_MARGIN)
    transmissionRange = np.max(trans)-np.min(trans)
    if (transmissionRange < 0.05):
          # force there to be a minimum range of transmission display
          # overemphasize tiny ozone lines
          transmissionRange = 0.05

    if (transmissionRange > 1 and transmissionRange < 10):
          # force there to be a minimum range of Tebbsky (10K) to display
          transmissionRange = 10

    # convert transmission to amplitude
    newtrans = y2 - (y2-y1)*(np.max(trans)-trans)/transmissionRange

    # Use edge values
    edgeValueTransmission = trans[-1]
    otherEdgeValueTransmission = trans[0]

    # Now convert the edge channels' transmission values into amplitude
    edgeValueAmplitude = y2 - (y2-y1)*(np.max(trans)-trans[-1])/transmissionRange
    otherEdgeValueAmplitude = y2 - (y2-y1)*(np.max(trans)-trans[0])/transmissionRange

    # Now convert amplitude to frame units, offsetting downward by half
    # the font size
    fontoffset = 0.01*subplotRows
    edgeValueFrame = (edgeValueAmplitude - lim[0])/yrange  - fontoffset
    otherEdgeValueFrame = (otherEdgeValueAmplitude - lim[0])/yrange  - fontoffset

    # scaleFactor is how large the plot is from the bottom x-axis
    # up to the labelgap, in units of the transmissionRange
    scaleFactor = (1+TOP_MARGIN*(1-labelgap)) / (TOP_MARGIN*(1-labelgap))

    # compute the transmission at the bottom of the plot, and label it
    y0transmission = np.max(trans) - transmissionRange*scaleFactor
    y0transmissionFrame = 0
    y0transmissionAmplitude = lim[0]

    if (y0transmission <= 0):
        # If the bottom of the plot is below zero transmission, then label
        # the location of zero transmission instead.
        if (debug):
            print("--------- y0transmission original = %f, (y1,y2)=(%f,%f)" % (y0transmission,y1,y2))
        y0transmissionAmplitude = y1-(y2-y1)*(np.min(trans)/transmissionRange)
        y0transmissionFrame = (y0transmissionAmplitude-lim[0]) / (lim[1]-lim[0])
        y0transmission = 0
    if (debug):
        print("-------- xframe=%d, scaleFactor = %s" % (xframe, str(scaleFactor)))
        print("edgeValueFrame, other = %s, %s" % (str(edgeValueFrame), str(otherEdgeValueFrame)))
        print("edgeValueTransmission, other = %s, %s" % (str(edgeValueTransmission), str(otherEdgeValueTransmission)))
        print("edgeValueAmplitude, otherEdgeValueAmplitude = %s, %s" % (str(edgeValueAmplitude), str(otherEdgeValueAmplitude)))
        print("y0transmission = %f, y0transmissionFrame = %f" % (y0transmission,y0transmissionFrame))
        print("y0transmissionAmplitude = %s" % (str(y0transmissionAmplitude)))
        print("transmissionRange = %s" % (str(transmissionRange)))
    return(newtrans, edgeValueFrame, y0transmission, y0transmissionFrame,
           otherEdgeValueFrame, edgeValueTransmission,
           otherEdgeValueTransmission, edgeValueAmplitude,
           otherEdgeValueAmplitude, y0transmissionAmplitude)

def RescaleX(chans, lim, plotrange, channels):
    # This function is now only used by DrawAtmosphere when xaxis='chan'.
    # It is only really necessary when len(chans)>MAX_ATM_CALC_CHANNELS.
    #  - September 2012
    # If the user specified a plotrange, then rescale to this range,
    # otherwise rescale to the automatically-determined range.

    # chans = 0..N where N=number of channels in the ATM_CALC
    # channels = 0..X where X=number of channels in the spw, regardless of flagging

    if (len(chans) != len(channels)):
        if (chans[1] > chans[0]):
            atmchanrange = chans[-1]-chans[0]
        else:
            atmchanrange = chans[0]-chans[-1]
        if len(channels) == 0:     ### added 2024Aug to prevent crash
            return(chans)          ### added 2024Aug to prevent crash
        if (channels[1] > channels[0]):
            chanrange = channels[-1]-channels[0]
        else:
            chanrange = channels[0]-channels[-1]

        newchans = np.array(chans)*chanrange/atmchanrange
        return(newchans)
    else:
        return(chans)

def recalcYlimitsFreq(chanrange, ylimits, amp, sideband,plotrange,xchannels,
                      debug=False,location=0,chanrangePercent=None):
    # Used by plots with xaxis='freq'
    # xchannels are the actual channel numbers of unflagged data, i.e. displayed points
    # amp is actual data plotted
    ylim_debug = False
    if (len(amp) < 1):
        return(pb.ylim()) # ylimits)
    if (chanrange[0]==0 and chanrange[1] == 0 and plotrange[2] == 0 and plotrange[3]==0
        and chanrangePercent == None):
        if (len(amp) == 1):
            if (ylim_debug):
                print("amp = %s" % (str(amp)))
            ylimits = [amp[0]-0.2, amp[0]+0.2]
        else:
            newmin = np.min(amp)
            newmax = np.max(amp)
            newmin = np.min([ylimits[0],newmin])
            newmax = np.max([ylimits[1],newmax])
            ylimits = [newmin, newmax]
    elif ((abs(chanrange[0]) > 0 or abs(chanrange[1]) > 0)):
        plottedChannels = np.intersect1d(xchannels, list(range(chanrange[0],chanrange[1]+1)))
        if (len(plottedChannels) < 1):
            return(ylimits)
        mylist = np.arange(xchannels.index(plottedChannels[0]), 1+xchannels.index(plottedChannels[-1]))
        if (mylist[-1] >= len(amp)):
            # prevent crash if many channels are flagged
            return(ylimits)
        if (ylim_debug):
            print("Starting with limits = %s" % (str(ylimits)))
            print("Examining channels: %s" % (str(mylist)))
            print("len(amp): %d" % (len(amp)))
            print("Examining values: amp[mylist] = %s" % (str(amp[mylist])))
        newmin = np.min(amp[mylist])
        newmax = np.max(amp[mylist])
        newmin = np.min([ylimits[0],newmin])
        newmax = np.max([ylimits[1],newmax])
        #  The following presents a problem with overlays, as it keeps widening forever
# #      newmin -= 0.05*(newmax-newmin)
# #      newmax += 0.05*(newmax-newmin)
        ylimits = [newmin, newmax]
    elif  (chanrangePercent != None):
      startFraction = (100-chanrangePercent)*0.5*0.01
      stopFraction = 1-(100-chanrangePercent)*0.5*0.01
      if (xchannels == []):
          # prevent crash if many channels are flagged: 2015-04-13
          return(ylimits)
      cr0 = int(np.round(np.max(xchannels)*startFraction))
      cr1 = int(np.round(np.max(xchannels)*stopFraction))
      plottedChannels = np.intersect1d(xchannels, list(range(cr0, cr1+1)))
      if (len(plottedChannels) < 1):
          return(ylimits)
      mylist = np.arange(xchannels.index(plottedChannels[0]), 1+xchannels.index(plottedChannels[-1]))
      if (mylist[-1] >= len(amp)):
          # prevent crash if many channels are flagged
          return(ylimits)
      if (ylim_debug):
          print("Starting with limits = ", ylimits)
          print("Examining channels: ", mylist)
          print("len(amp): %d" % (len(amp)))
          print("type(amp) = %s" % (str(type(amp))))
          print("Examining values: amp[mylist] = %s" % (str(amp[mylist])))
      newmin = np.min(amp[mylist])
      newmax = np.max(amp[mylist])
      newmin = np.min([ylimits[0],newmin])
      newmax = np.max([ylimits[1],newmax])
      ylimits = [newmin, newmax]
    if (ylim_debug):
        print("Returning with limits = %s" % (str(ylimits)))
    return ylimits

def recalcYlimits(plotrange, ylimits, amp):
    # Used by plots with xaxis='chan'
    if (len(amp) < 1):
        return(pb.ylim())
    if ((abs(plotrange[0]) > 0 or abs(plotrange[1]) > 0) and (plotrange[2] == 0 and plotrange[3] == 0)):
        x0 = int(plotrange[0])
        x1 = int(plotrange[1])
        if (x0 < 0):
            x0 = 0
        if (x1 > len(amp)-1):
            x1 = len(amp)-1
        if (len(amp) > x1 and x0 < x1):
            newmin = np.min(amp[x0:x1])
            newmax = np.max(amp[x0:x1])
            newmin = np.min([ylimits[0],newmin])
            newmax = np.max([ylimits[1],newmax])
            ylimits = [newmin, newmax]
    else:
        ylimits = pb.ylim()  # added on 10/27/2011
# #      print("current ylimits = ", ylimits)
    return(ylimits)

def SetNewYLimits(newylimits):
#    print("Entered SetNewYLimits with ", newylimits )
    newrange = newylimits[1]-newylimits[0]
    if (newrange > 0):
        pb.ylim([newylimits[0]-0.0*newrange, newylimits[1]+0.0*newrange])

def SetNewXLimits(newxlimits, loc=0):
#    print("loc=%d: Entered SetNewXLimits with range = %.3f (%f-%f)" % (loc,np.max(newxlimits)-np.min(newxlimits), newxlimits[0], newxlimits[1]))
    myxrange = np.abs(newxlimits[1]-newxlimits[0])
    if (myxrange == 0):
        myxrange = 0.001
    mybuffer = 0.01
    if (newxlimits[0] < newxlimits[1]):
        pb.xlim([newxlimits[0]-myxrange*mybuffer,newxlimits[1]+myxrange*mybuffer] )
    else:
#        print("Swapping xlimits order")
        pb.xlim(newxlimits[1]-myxrange*mybuffer, newxlimits[0]+myxrange*mybuffer)

def sloppyMatch(newvalue, mylist, threshold, mytime=None, scansToPlot=[],
                scansForUniqueTimes=[], myprint=False, whichone=False):
    """
    If scan numbers are present, perform an exact match, otherwise compare the
    time stamps of the solutions.
    """
    debug = myprint
    if (debug):
        print("sloppyMatch: scansToPlot = %s" % (str(scansToPlot)))
    mymatch = None
    if (len(scansToPlot) > 0):
        if (mytime >= len(scansForUniqueTimes)):
            print("sloppyMatch() mytime is too large:  mytime=%d >= len(scansForUniqueTimes)=%d: " % (mytime, len(scansForUniqueTimes)), scansForUniqueTimes)
        matched = scansForUniqueTimes[mytime] in scansToPlot
        if (whichone or myprint):
            myscan = scansForUniqueTimes[mytime]
            if (myscan in scansToPlot):
                mymatch = list(scansToPlot).index(myscan)
        if (matched == False and myprint==True):
            print("sloppyMatch: %d is not in %s" % (myscan, list(scansToPlot)))
        elif (myprint==True):
            print("sloppyMatch: %d is in %s" % (myscan, list(scansToPlot)))
    else:
        matched = False
        if (type(mylist) != list and type(mylist)!=np.ndarray):
            mylist = [mylist]
        mymatch = -1
        for i in range(len(mylist)):
            v = mylist[i]
            if (abs(newvalue-v) < threshold):
                matched = True
                mymatch = i
        if (matched == False and myprint==True):
            print("sloppyMatch: %.0f is not within %.0f of anything in %s" % (newvalue,threshold, str([int(round(b)) for b in mylist])))
        elif (myprint==True):
            print("sloppyMatch: %.0f is within %.0f of something in %s" % (newvalue,threshold, str([int(round(b)) for b in mylist])))
    if (whichone ==  False):
        return(matched)
    else:
        return(matched,mymatch)

# replacement function with extra optional parameter added on 2024Aug
def sloppyUnique(t, thresholdSeconds, cal_scans=None):
    """
    Takes a list of numbers and returns a list of unique values, subject 
    to a threshold difference.
    cal_scans: if specified, then perform the analysis per scan number
    """
    # start with the first entry (t[0]), and only add a new entry if it is more than the threshold from prior
    if cal_scans is None:
        sloppyList = [t[0]]
        for i in range(1,len(t)):
            keepit = True
    #        for j in range(0,i):   # prior to PIPE-1519
    #            if (abs(t[i]-t[j]) < thresholdSeconds):
            for j in range(len(sloppyList)):
                if (abs(t[i]-sloppyList[j]) < thresholdSeconds):
                    keepit = False
            if (keepit):
                sloppyList.append(t[i])
    else:
        # build a list of one time per calibration scan
        uniqueCalScans = np.unique(cal_scans)
        t = np.array(t)
        sloppyList = []
#        print("len(t) = %d, len(cal_scans) = %d" % (len(t),len(cal_scans)))
        for k,calscan in enumerate(uniqueCalScans):
            times = t[np.where(calscan == cal_scans)]
            sloppyList.append(times[0])
#    print "sloppyUnique returns %d values from the original %d" % (len(sloppyList), len(t))
    return(sloppyList)

# commented out 2024Aug
#def sloppyUnique(t, thresholdSeconds):
#    """
#    Takes a list of numbers and returns a list of unique values, subject to a threshold difference.
#    """
#    # start with the first entry, and only add a new entry if it is more than the threshold from prior
#    sloppyList = [t[0]]
#    for i in range(1,len(t)):
#        keepit = True
#        for j in range(0,i):
#            if (abs(t[i]-t[j]) < thresholdSeconds):
#                keepit = False
#        if (keepit):
#            sloppyList.append(t[i])
##    print("sloppyUnique returns %d values from the original %d" % (len(sloppyList), len(t)))
#    return(sloppyList)

def SetLimits(plotrange, chanrange, newylimits, channels, frequencies, pfrequencies,
              ampMin, ampMax, xaxis, pxl, chanrangeSetXrange, chanrangePercent=None):
    """
    This is the place where chanrange actually takes effect.
    """
    if (abs(plotrange[0]) > 0 or abs(plotrange[1]) > 0):
        SetNewXLimits([plotrange[0],plotrange[1]])
        if (plotrange[2] == 0 and plotrange[3] == 0):
            # reset the ylimits based on the channel range shown (selected via plotrange)
            SetNewYLimits(newylimits)
    else: # set xlimits to full range
        if (xaxis.find('chan')>=0):
            SetNewXLimits([channels[0],channels[-1]])
        else:
            if (chanrangeSetXrange or (chanrange[0]==0 and chanrange[1]==0 and chanrangePercent==None)): # CAS-7965
                # print("SetLimits(): Setting x limits to full range (%f-%f)" % (frequencies[0], frequencies[-1]))
                SetNewXLimits([frequencies[0], frequencies[-1]])
    if (chanrange[0] != 0 or chanrange[1] != 0 or chanrangePercent != None):
        # reset the ylimits based on the channel range specified (selected via chanrange)
        if (newylimits != [LARGE_POSITIVE, LARGE_NEGATIVE]):
            SetNewYLimits(newylimits)
#        print("pxl=%d, chanrange[0]=%d, chanrange[1]=%d, shape(pfreq), shape(freq)=" % (pxl, chanrange[0], chanrange[1]), np.shape(pfrequencies),np.shape(frequencies))
        # Use frequencies instead of pfrequencies, because frequencies are not flagged and
        # will continue to work if chanranze is specified and data are flagged.
        if chanrangeSetXrange:
            if (chanrangePercent == None):
                try:
                    SetNewXLimits([frequencies[chanrange[0]], frequencies[chanrange[1]]])  # Apr 3, 2012
                except:
                    print("a)Invalid chanrange (%d-%d). Valid range = 0-%d" % (chanrange[0],chanrange[1],len(frequencies)-1))
                    return(-1)
            else:
                startFraction = (100-chanrangePercent)*0.5*0.01
                stopFraction = 1-(100-chanrangePercent)*0.5*0.01
                cr0 = int(np.round(np.max(channels)*startFraction))
                cr1 = int(np.round(np.max(channels)*stopFraction))
                try:
                    SetNewXLimits([frequencies[cr0], frequencies[cr1]])
                except:
                    print("b)Invalid chanrange (%d-%d). Valid range = 0-%d" % (cr0,cr1,len(frequencies)-1))
                    return(-1)
    if (abs(plotrange[2]) > 0 or abs(plotrange[3]) > 0):
        SetNewYLimits([plotrange[2],plotrange[3]])
    return(0)

def showFDM(originalSpw, chanFreqGHz, baseband, showBasebandNumber, basebandDict, overlayColors):
    """
    Draws a horizontal bar indicating the location of FDM spws in the dataset.

    Still need to limit based on the baseband -- need dictionary passed in.
    originalSpw: should contain all spws in the dataset, not just the ones
                in the caltable
    baseband: the baseband of the current spw
    showBasebandNumber: force the display of all FDM spws, and their baseband number
    basebandDict: {1:[17,19], 2:[21,23], etc.}  or {} for really old datasets
    """

    # add some space at the bottom -- Apr 25, 2012
    ylim = pb.ylim()
    yrange = ylim[1]-ylim[0]
    pb.ylim([ylim[0]-BOTTOM_MARGIN*yrange, ylim[1]])

    sdebug = False
    if (sdebug):
        print(("Showing FDM (%d)" % (len(originalSpw)), originalSpw))
        print("baseband = %d, basebandDict = %s" % (baseband, str(basebandDict)))
    fdmctr = -1
    x0,x1 = pb.xlim()
    y0,y1 = pb.ylim()
    yrange = y1 - y0
    myxrange = x1 - x0
    labelAbove = False  # False means label to the right
    for i in range(len(originalSpw)):
        nchan = len(chanFreqGHz[i])
        # latter 3 values are for ACA with FPS enabled
        if (nchan >= 15 and nchan not in [256,128,64,32,16,248,124,62]):
          if (originalSpw[i] in basebandDict[baseband] or showBasebandNumber):
            fdmctr += 1
            verticalOffset = fdmctr*0.04*yrange
            y1a = y0 + 0.03*yrange + verticalOffset
            if (labelAbove):
                y2 = y1a + 0.01*yrange
            else:
                y2 = y1a - 0.016*yrange
#            print("chan=%d: Drawing line at y=%f (y0=%f) from x=%f to %f" % (len(chanFreqGHz[i]),
#                                              y1a,y0,chanFreqGHz[i][0], chanFreqGHz[i][-1]))
            f0 = chanFreqGHz[i][0]
            f1 = chanFreqGHz[i][-1]
            if (f1 < f0):
                swap = f1
                f1 = f0
                f0 = swap
            v0 = np.max([f0,x0])
            v1 = np.min([f1,x1])
            if (v1 > v0):
                if (labelAbove):
                    xlabel = 0.5*(v0+v1)
                    if (xlabel < x0):
                        xlabel = x0
                    if (xlabel > x1):
                        xlabel = x1
                else:
                    xlabel = v1+0.02*myxrange
                pb.plot([v0,v1], [y1a,y1a], '-',
                        linewidth=4, color=overlayColors[fdmctr],markeredgewidth=markeredgewidth)
                if (showBasebandNumber):
                    mybaseband = [key for key in basebandDict if i in basebandDict[key]]
                    if (len(mybaseband) > 0):
                        pb.text(xlabel, y2, "spw%d(bb%d)"%(i,mybaseband[0]), size=7)
                    else:
                        pb.text(xlabel, y2, "spw%d(bb?)"%(i), size=7)
                else:
                    pb.text(xlabel, y2, "spw%d"%(i), size=7)
                if (sdebug): print("Plotting spw %d (%d)" % (i, originalSpw[i]))
            else:
                if (sdebug): print("Not plotting spw %d (%d) because %f < %f" % (i,originalSpw[i],v0,v1))
          else:
              if (sdebug): print("Not plotting spw %d (%d) because it is not in baseband %d (%s)" % (i,originalSpw[i],baseband,basebandDict[baseband]))
        else:
            if (sdebug): print("Not plotting spw %d (%d) because fewer than 256 channels (%d)" % (i,originalSpw[i],nchan))
    if (fdmctr > -1):
        pb.ylim([y0,y1])
        pb.xlim([x0,x1])

def DrawAtmosphere(showatm, showtsky, subplotRows, atmString, mysize,
                   TebbSky, plotrange, xaxis, atmchan, atmfreq, transmission,
                   subplotCols, lo1='', xframe=0, firstFrame=0,
                   showatmPoints=False, channels=[0], mylineno=-1,xant=-1,
                   overlaySpws=False, overlayBasebands=False, drewAtmosphere=False,
                   loc=-1, showtsys=False, Trx=None):
    """
    Draws atmospheric transmission or Tsky on an amplitude vs. chan or freq plot.
    """
    xlim = pb.xlim()
    ylim = pb.ylim()
    myxrange = xlim[1]-xlim[0]
    yrange = ylim[1]-ylim[0]

    if (not drewAtmosphere and not overlayBasebands):  # CAS-8489 final
        if (lo1 == ''):
            # add some space at the top -- Apr 16, 2012
            pb.ylim([ylim[0], ylim[1]+TOP_MARGIN*yrange])
        else:
            pb.ylim([ylim[0], ylim[1]+TOP_MARGIN*yrange*0.5])
        ylim = pb.ylim()
    yrange = ylim[1]-ylim[0]
    #
    ystartPolLabel = 1.0-0.04*subplotRows
    if (lo1 == ''):
        transmissionColor = 'm'
        tskyColor = 'm'
    else:
        transmissionColor = 'k'
        tskyColor = 'k'
    if (showatmPoints):
        atmline = '.'
    else:
        atmline = '-'
    if (showatm or showtsky):
        if (showatm):
            atmcolor = transmissionColor
        else:
            atmcolor = tskyColor
        if (lo1 == '' and not drewAtmosphere):
            pb.text(0.25, ystartPolLabel, atmString, color=atmcolor, size=mysize, transform=pb.gca().transAxes)

        if (showtsky):
            if showtsys:
                rescaledY = TebbSky
                if Trx == 'auto':
                    scaleFactor = np.mean([ylim[1]-np.max(TebbSky), ylim[0]-np.min(TebbSky)])
                    scaleFactor = 0.5*(ylim[1]+ylim[0]) - np.mean(TebbSky)
                    rescaledY += scaleFactor
            else:
                rescaledY, edgeYvalue, zeroValue, zeroYValue, otherEdgeYvalue, edgeT, otherEdgeT, edgeValueAmplitude, otherEdgeValueAmplitude, zeroValueAmplitude = RescaleTrans(TebbSky, ylim, subplotRows, lo1, xframe)

        else:
            rescaledY, edgeYvalue, zeroValue, zeroYValue, otherEdgeYvalue, edgeT, otherEdgeT, edgeValueAmplitude, otherEdgeValueAmplitude, zeroValueAmplitude = RescaleTrans(transmission, ylim, subplotRows, lo1, xframe)
        if (overlayBasebands and xaxis.find('freq')>=0):
            # use axis coordinates for y-axis only so that transmission can be on common scale
            trans = matplotlib.transforms.blended_transform_factory(pb.gca().transData, pb.gca().transAxes)
            if showtsky:
                pb.plot(atmfreq, TebbSky/300., '%s%s'%(atmcolor,atmline),
                        markeredgewidth=markeredgewidth, transform=trans)
            else:
                pb.plot(atmfreq, transmission, '%s%s'%(atmcolor,atmline),
                        markeredgewidth=markeredgewidth, transform=trans)
            if (atmfreq[0]<atmfreq[1]):
                tindex = -1
            else:
                tindex = 0
        else:
            # use user coordinates
            if (xaxis.find('chan')>=0):
                rescaledX = RescaleX(atmchan, xlim, plotrange, channels)
    #            rescaledX = atmchan
                pb.plot(rescaledX, rescaledY,'%s%s'%(atmcolor,atmline),markeredgewidth=markeredgewidth)
                tindex = -1
            elif (xaxis.find('freq')>=0):
                pb.plot(atmfreq, rescaledY, '%s%s'%(atmcolor,atmline),markeredgewidth=markeredgewidth)
                if (atmfreq[0]<atmfreq[1]):
                    tindex = -1
                else:
                    tindex = 0
        if (lo1 == ''):
            xEdgeLabel = 1.01
        else:
            if (xframe == firstFrame):
                xEdgeLabel = -0.10*subplotCols # avoids overwriting y-axis label
            else:
                xEdgeLabel = -0.10*subplotCols
        SetNewXLimits(xlim)  # necessary for zoom='intersect'
        if (not overlayBasebands):   # CAS-8489 final
            SetNewYLimits(ylim)
        # Now draw the percentage on right edge of plot
        if (not drewAtmosphere):
            if (overlayBasebands and xaxis.find('freq')>=0):   # CAS-8489 final
                trans = matplotlib.transforms.blended_transform_factory(pb.gca().transData, pb.gca().transAxes)
                zeroValue = 0
                zeroValueAmplitude = 0
                edgeValueAmplitude = 1
                if (showtsky):
                    edgeT = 300
                    if (lo1 == ''):
                        pb.text(xlim[1]+0.06*myxrange/subplotCols, edgeValueAmplitude,
                                '%.0fK'%(edgeT), color=atmcolor, size=mysize, transform=trans)
                        pb.text(xlim[1]+0.06*myxrange/subplotCols, zeroValueAmplitude,
                                '%.0fK'%(zeroValue), color=atmcolor, transform=trans,
                                size=mysize)
                    else:
                        pb.text(xEdgeLabel, edgeValueAmplitude,'%.0fK'%(edgeT),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
                        pb.text(xEdgeLabel, zeroValueAmplitude,'%.0fK'%(zeroValue),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
                else:
                    # showatm=True
                    edgeT = 1
                    if (lo1 == ''):
                        pb.text(xlim[1]+0.05*myxrange/subplotCols, edgeValueAmplitude,
                                '%.0f%%'%(edgeT*100), color=atmcolor, size=mysize,
                                transform=trans, va='center')
                        pb.text(xlim[1]+0.05*myxrange/subplotCols, zeroValueAmplitude,
                                '%.0f%%'%(zeroValue*100), color=atmcolor, transform=trans,
                                size=mysize, va='center')
                    else:
                        pb.text(xEdgeLabel, edgeValueAmplitude,'%.0f%%'%(edgeT*100),
                                color=atmcolor, va='center',
                                size=mysize, transform=pb.gca().transAxes)
                        pb.text(xEdgeLabel, zeroValueAmplitude,'%.0f%%'%(zeroValue*100),
                                color=atmcolor, va='center',
                                size=mysize, transform=pb.gca().transAxes)
            elif not showtsys:
                if (showtsky):
                    if (lo1 == ''):
                        # This must be done in user coordinates since another curve
                        # is plotted following this one.
                        pb.text(xlim[1]+0.06*myxrange/subplotCols, edgeValueAmplitude,
                                '%.0fK'%(edgeT), color=atmcolor, size=mysize)
                        pb.text(xlim[1]+0.06*myxrange/subplotCols, zeroValueAmplitude,
                                '%.0fK'%(zeroValue), color=atmcolor,
                                size=mysize)
                    else:
                        # This can remain in axes units since it is the final plot.
                        pb.text(xEdgeLabel, otherEdgeYvalue,'%.0fK'%(otherEdgeT),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
                        pb.text(xEdgeLabel, zeroYValue,'%.0fK'%(zeroValue),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
                else:
                    # showatm=True
                    if (lo1 == ''):
                        # This must be done in user coordinates since another curve
                        # is plotted following this one.
                        pb.text(xlim[1]+0.05*myxrange/subplotCols, edgeValueAmplitude,
                                '%.0f%%'%(edgeT*100), color=atmcolor, size=mysize)
                        pb.text(xlim[1]+0.05*myxrange/subplotCols, zeroValueAmplitude,
                                '%.0f%%'%(zeroValue*100), color=atmcolor,
                                size=mysize)
                    else:
                        # This can remain in axes units since it is the final plot.
                        pb.text(xEdgeLabel, otherEdgeYvalue,'%.0f%%'%(otherEdgeT*100),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
                        pb.text(xEdgeLabel, zeroYValue,'%.0f%%'%(zeroValue*100),
                                color=atmcolor,
                                size=mysize, transform=pb.gca().transAxes)
        if (lo1 != ''):
            if (xframe == firstFrame):
                pb.text(+1.04-0.04*subplotCols, -0.07*subplotRows,
                        'Signal SB', color='m', size=mysize,
                        transform=pb.gca().transAxes)
                pb.text(-0.03-0.08*subplotCols, -0.07*subplotRows,
                        'Image SB', color='k', size=mysize,
                        transform=pb.gca().transAxes)
#                pb.text(+0.96-0.08*subplotCols, -0.07*subplotRows,
#                        'Signal Sideband', color='m', size=mysize,
#                        transform=pb.gca().transAxes)
#                pb.text(-0.08*subplotCols, -0.07*subplotRows,
#                        'Image Sideband', color='k', size=mysize,
#                        transform=pb.gca().transAxes)
    return ylim # CAS-8655

def DrawBottomLegendPageCoords(msName, uniqueTimesMytime, mysize, figfile):
    msName = msName.split('/')[-1]
    bottomLegend = msName + '  ObsDate=' + utdatestring(uniqueTimesMytime)
    if (os.path.basename(figfile).find('regression') == 0):
        regression = True
    else:
        regression = False
    if (regression == False):
        bottomLegend += '   plotbandpass v' \
                  + PLOTBANDPASS_REVISION_STRING.split()[2] + ' = ' \
                  + PLOTBANDPASS_REVISION_STRING.split()[3] + ' ' \
                  + PLOTBANDPASS_REVISION_STRING.split()[4]
#    The following should be used going forward, as it is better for long VLA names
    pb.text(0.04, 0.02, bottomLegend, size=mysize, transform=pb.gcf().transFigure)
#    pb.text(0.1, 0.02, bottomLegend, size=mysize, transform=pb.gcf().transFigure)

def DrawAntennaNames(msAnt, antennasToPlot, msFound, mysize, overlayColors):
    for a in range(len(antennasToPlot)):
        if (msFound):
            legendString = msAnt[antennasToPlot[a]]
        else:
            legendString = str(antennasToPlot[a])
        if (a<maxAntennaNamesAcrossTheTop):
            x0 = xstartTitle+(a*antennaHorizontalSpacing)
            y0 = ystartOverlayLegend
        else:
            # start going down the righthand side
            x0 = xstartTitle+(maxAntennaNamesAcrossTheTop*antennaHorizontalSpacing)
            y0 = ystartOverlayLegend-(a-maxAntennaNamesAcrossTheTop)*antennaVerticalSpacing
        pb.text(x0, y0, legendString,color=overlayColors[a],fontsize=mysize,
                transform=pb.gcf().transFigure)

def stdInfo(a, sigma=3, edge=0, spw=-1, xant=-1, pol=-1):
    """
    Computes the standard deviation of a list, then returns the value, plus the
    number and list of channels that exceed sigma*std, and the worst outlier.
    """
    info = {}
    if (edge >= len(a)//2):  # protect against too large of an edge value
        originalEdge = edge
        if (len(a) == 2*(len(a)//2)):
            edge = len(a)//2 - 1 # use middle 2 points
        else:
            edge = len(a)//2  # use central point
        if (edge < 0):
            edge = 0
        print("stdInfo: WARNING edge value is too large for spw%d xant%d pol%d, reducing it from %d to %d." % (spw, xant, pol, originalEdge, edge))
    info['std'] = np.std(a[edge:len(a)-edge])
    chan = []
    outlierValue = 0
    outlierChannel = None
    for i in range(edge,len(a)-edge):
        if (np.abs(a[i]) > sigma*info['std']):
            chan.append(i)
        if (np.abs(a[i]) > np.abs(outlierValue)):
            outlierValue = a[i]
            outlierChannel = i
    info['nchan'] = len(chan)
    info['chan'] = chan
    info['outlierValue'] = outlierValue/info['std']
    info['outlierChannel'] = outlierChannel
    return(info)

def madInfo(a, madsigma=3, edge=0):
    """
    Computes the MAD of a list, then returns the value, plus the number and list
    of channels that exceed madsigma*MAD, and the worst outlier.
    """
    info = {}
    if (edge >= len(a)//2):  # protect against too large of an edge value
        originalEdge = edge
        if (len(a) == 2*(len(a)//2)):
            edge = len(a)//2 - 1 # use middle 2 points
        else:
            edge = len(a)//2  # use central point
        print("WARNING edge value is too large, reducing it from %d to %d." % (originalEdge, edge))
    info['mad'] = mad(a[edge:len(a)-edge])
    chan = []
    outlierValue = 0
    outlierChannel = None
    for i in range(edge,len(a)-edge):
        if (np.abs(a[i]) > madsigma*info['mad']):
            chan.append(i)
        if (np.abs(a[i]) > np.abs(outlierValue)):
            outlierValue = a[i]
            outlierChannel = i
    info['nchan'] = len(chan)
    info['chan'] = chan
    info['outlierValue'] = outlierValue/info['mad']
    info['outlierChannel'] = outlierChannel
    return(info)

def platformingCheck(a, threshold=DEFAULT_PLATFORMING_THRESHOLD):
    """
    Checks for values outside the range of +-threshold.
    Meant to be passed an amplitude spectrum.
    """
    info = {}
    startChan = len(a)/32. - 1
    endChan = len(a)*31/32. + 1
#    print("Checking channels %d-%d for platforming" % (startChan,endChan))
    if (startChan <= 0 or endChan >= len(a)):
        return
    middleChan = (startChan+endChan)//2
    channelRange1 = list(range(startChan,middleChan+1))
    channelRange2 = list(range(endChan,middleChan,-1))
    platforming = False
    awayFromEdge = False
    for i in channelRange1:
        if (np.abs(a[i]) > threshold):
            if (awayFromEdge):
#                print("a[%d]=%f" % (i,a[i]))
                platforming = True
                return(platforming)
        else:
            awayFromEdge = True
    awayFromEdge = False
    for i in channelRange2:
        if (np.abs(a[i]) > threshold):
            if (awayFromEdge):
                platforming = True
                return(platforming)
        else:
            awayFromEdge = True
    return(platforming)

def mad(a, c=0.6745, axis=0):
    """
    Median Absolute Deviation along given axis of an array:

    median(abs(a - median(a))) / c

    c = 0.6745 is the constant to convert from MAD to std; it is used by
    default

    """
    a = np.array(a)
    good = (a==a)
    a = np.asarray(a, np.float64)
    if a.ndim == 1:
        d = np.median(a[good])
        m = np.median(np.fabs(a[good] - d) / c)
#        print( "mad = %f" % (m))
    else:
        d = np.median(a[good], axis=axis)
        # I don't want the array to change so I have to copy it?
        if axis > 0:
            aswp = swapaxes(a[good],0,axis)
        else:
            aswp = a[good]
        m = np.median(np.fabs(aswp - d) / c, axis=0)

    return m

def callFrequencyRangeForSpws(mymsmd, spwlist, vm, caltable=None):
    """
    Returns the min and max frequency of a list of spws.
    Uses msmd, unless the ms is not found, in which case it uses
    the spw information inside the (new-style) cal-table.
    """
    if (mymsmd != '' and ctsys.compare_version('>=',[4,1,0])):
        return(frequencyRangeForSpws(mymsmd,spwlist))
    else:
        freqs = []
        if (type(vm) != str):
            for spw in spwlist:
                freqs += list(vm.spwInfo[spw]["chanFreqs"])
        else:
            mytb = table()
            try:
                mytb.open(caltable+'/SPECTRAL_WINDOW')
                chanfreq = []
                if (len(spwlist) == 0): # CAS-8489b
                    originalSpws = list(range(len(mytb.getcol('MEAS_FREQ_REF'))))
                    spwlist = originalSpws
                for i in spwlist:  # CAS-8489b
                    # The array shapes can vary.
                    chanfreq.append(mytb.getcell('CHAN_FREQ',i))
                for cf in chanfreq:
                    freqs += list(cf)
                mytb.close()
            except:
                mytb.done()
        if (freqs == []):
            return(0,0)
        else:
            return(np.min(freqs)*1e-9, np.max(freqs)*1e-9)

def frequencyRangeForSpws(mymsmd, spwlist):
    """
    Returns the min and max frequency of a list of spws.
    """
    allfreqs = []
    for spw in spwlist:
        allfreqs += list(mymsmd.chanfreqs(spw))
    if (len(allfreqs) == 0):
        return(0,0)
    return(np.min(allfreqs)*1e-9, np.max(allfreqs)*1e-9)

def buildSpwString(overlaySpws, overlayBasebands, spwsToPlot, ispw, originalSpw,
                   observatoryName, baseband, showBasebandNumber):
    if (overlayBasebands):
        spwString = ' all'
    elif (overlaySpws and len(spwsToPlot)>1):
        if (observatoryName.find('ALMA') >= 0 or observatoryName.find('ACA') >= 0):
            # show a list of all spws
            spwString = str(spwsToPlot).replace(' ','').strip('[').strip(']')
        else:
            # show the range of spw numbers
            spwString = '%2d-%2d' % (np.min(spwsToPlot),np.max(spwsToPlot))
    elif (ispw==originalSpw):
        spwString = '%2d' % (ispw)
    else:
        spwString = '%2d (%d)' % (ispw,originalSpw)
    if (overlayBasebands==False):
        spwString = appendBasebandNumber(spwString, baseband, showBasebandNumber)
    return(spwString)

def appendBasebandNumber(spwString, baseband, showBasebandNumber):
    if (showBasebandNumber):
        spwString += ', bb%d' % (baseband)
    return(spwString)

def getSpwsForBaseband(vis, bb, mymsmd=None):
        needToClose = False
#    if (casadef.subversion_revision >= 25753):
        if (mymsmd is None or mymsmd == ''):
            needToClose = True
            mymsmd = msmetadata()
            mymsmd.open(vis)
        s = mymsmd.spwsforbaseband(bb)
        if needToClose:
            mymsmd.close()
        return(s)
#    else:
#        return(getBasebandDict(vis,caltable=caltable,mymsmd=mymsmd))

def getBasebandDict(vis=None, spwlist=[], caltable=None, mymsmd=None):
    """
    Builds a dictionary with baseband numbers as the keys and the
    associated spws as the values.  The optional parameter spwlist can
    be used to restrict the contents of the dictionary.
    Note: This is obsoleted by msmd.spwsforbaseband(-1)
    """
    bbdict = {}
    if (vis != None):
        if (os.path.exists(vis)):
            bbs = getBasebandNumbers(vis)
        elif (caltable != None):
            bbs = getBasebandNumbersFromCaltable(caltable)
        else:
            print("Must specify either vis or caltable")
            return
    elif (caltable != None):
        bbs = getBasebandNumbersFromCaltable(caltable)
    else:
        print("Must specify either vis or caltable")
        return
    if (type(bbs) == int):  # old datasets will bomb on msmd.baseband()
        return(bbdict)
    if (ctsys.compare_version('>=',[4,1,0]) and vis != None):
        if (os.path.exists(vis)):
            needToClose = False
            if mymsmd is None or mymsmd == '':
                needToClose = True
                mymsmd = msmetadata()
                mymsmd.open(vis)
            if (spwlist == []):
                nspws = mymsmd.nspw()
                spwlist = list(range(nspws))
            for spw in spwlist:
                bbc_no = mymsmd.baseband(spw)
                if (bbc_no not in list(bbdict.keys())):
                    bbdict[bbc_no] = [spw]
                else:
                    bbdict[bbc_no].append(spw)
            if needToClose:
                mymsmd.close()
    if (bbdict == {}):
        # read from spw table
        ubbs = np.unique(bbs)
        for bb in ubbs:
            bbdict[bb] = []
        for i in range(len(bbs)):
            bbdict[bbs[i]].append(i)
    return(bbdict)

def getBasebandNumbersFromCaltable(caltable) :
    """
    Returns the baseband numbers associated with each spw in
    the specified caltable.
    Todd Hunter
    """
    if (os.path.exists(caltable) == False):
        print("getBasebandNumbersFromCaltable(): caltable set not found")
        return -1
    mytb = table()
    mytb.open(caltable)
    spectralWindowTable = mytb.getkeyword('SPECTRAL_WINDOW').split()[1]
    mytb.close()
    mytb.open(spectralWindowTable)
    if ("BBC_NO" in mytb.colnames()):
        bbNums = mytb.getcol("BBC_NO")
    else:
        # until CAS-6853 is solved, need to get it from the name
#        print("BBC_NO not in colnames (CAS-6853).  Using NAME column.")
        names = mytb.getcol('NAME')
        bbNums = []
        trivial = True
        for name in names:
            if (name.find('#BB_') > 0):
                bbNums.append(int(name.split('#BB_')[1].split('#')[0]))
                trivial = False
            else:
                bbNums.append(-1)
        if (trivial): bbNums = -1
    mytb.close()
    return bbNums


def getLOs(inputMs, verbose=True):
    """
    Reads the LO information from an ms's ASDM_RECEIVER table.  It returns
    a list of 7 lists: [freqLO,band,spws,names,sidebands,receiverIDs,spwnames]
    The logic for converting this raw list into sensible association with
    spw numbers is in printLOs().  These lists are longer than the true number
    of spws by Nantennas-1 due to the extra WVR spws.
    -Todd Hunter
    """
    if (os.path.exists(inputMs)):
        mytb = table()
        if (os.path.exists("%s/ASDM_RECEIVER" % inputMs)):
            try:
                mytb.open("%s/ASDM_RECEIVER" % inputMs)
            except:
                print("Could not open the existing ASDM_RECEIVER table")
                mytb.close()
                return([])
        else:
            if (os.path.exists(inputMs+'/ASDMBinary')):
                print("This is an ASDM, not an ms!  Use printLOsFromASDM.")
            else:
                if (verbose):
                    print("The ASDM_RECEIVER table for this ms does not exist.")
            mytb.close()
            return([])
    else:
        print("This ms does not exist = %s." % (inputMs))
        return([])

    numLO = mytb.getcol('numLO')
    freqLO = []
    band = []
    spws = []
    names = []
    sidebands = []
    receiverIds = []
    for i in range(len(numLO)):
        spw = int((mytb.getcell('spectralWindowId',i).split('_')[1]))
        if (spw not in spws):
            spws.append(spw)
            freqLO.append(mytb.getcell('freqLO',i))
            band.append(mytb.getcell('frequencyBand',i))
            names.append(mytb.getcell('name',i))
            sidebands.append(mytb.getcell('sidebandLO',i))
            receiverIds.append(int(mytb.getcell('receiverId',i)))
    mytb.close()
    mytb.open("%s/SPECTRAL_WINDOW" % inputMs)
    spwNames = mytb.getcol("NAME")
    mytb.close()
    mytb.close()
    return([freqLO,band,spws,names,sidebands,receiverIds,spwNames])

def readPWVFromASDM_CALATMOSPHERE(vis):
    """
    Reads the PWV via the water column of the ASDM_CALATMOSPHERE table.
    - Todd Hunter
    """
    mytb = table()
    mytb.open("%s/ASDM_CALATMOSPHERE" % vis)
    pwvtime = mytb.getcol('startValidTime')  # mjdsec
    antenna = mytb.getcol('antennaName')
    pwv = mytb.getcol('water')[0]  # There seem to be 2 identical entries per row, so take first one.
    mytb.close()
    return(pwvtime, antenna, pwv)

def getMedianPWV(vis='.', myTimes=[0,999999999999], asdm='', verbose=False):
    """
    Extracts the PWV measurements from the WVR on all antennas for the
    specified time range.  The time range is input as a two-element list of
    MJD seconds (default = all times).  First, it tries to find the ASDM_CALWVR
    table in the ms.  If that fails, it then tries to find CalWVR.xml in the
    specified ASDM, or failing that, an ASDM of the same name (-.ms).  If neither of
    these exist, then it tries to find CalWVR.xml in the present working directory.
    If it still fails, it looks for CalWVR.xml in the .ms directory.  Thus,
    you only need to copy this xml file from the ASDM into your ms, rather
    than the entire ASDM. Returns the median and standard deviation in millimeters.
    For further help and examples, see https://safe.nrao.edu/wiki/bin/view/ALMA/GetMedianPWV
    -- Todd Hunter
    """
    pwvmean = 0
    success = False
    mytb = table()
    if (verbose):
        print("in getMedianPWV with myTimes = %s" % (str(myTimes)))
    try:
      if (os.path.exists("%s/ASDM_CALWVR"%vis)):
          mytb.open("%s/ASDM_CALWVR" % vis)
          pwvtime = mytb.getcol('startValidTime')  # mjdsec
          antenna = mytb.getcol('antennaName')
          pwv = mytb.getcol('water')
          mytb.close()
          success = True
          if (len(pwv) < 1):
              if (os.path.exists("%s/ASDM_CALATMOSPHERE" % vis)):
                  pwvtime, antenna, pwv = readPWVFromASDM_CALATMOSPHERE(vis)
                  success = True
                  if (len(pwv) < 1):
                      print("Found no data in ASDM_CALWVR nor ASDM_CALATMOSPHERE table")
                      return(0,-1)
              else:
                  if (verbose):
                      print("Did not find ASDM_CALATMOSPHERE in the ms")
                  return(0,-1)
          if (verbose):
              print("Opened ASDM_CALWVR table, len(pwvtime)=%s" % (str(len(pwvtime))))
      else:
          if (verbose):
              print("Did not find ASDM_CALWVR table in the ms. Will look for ASDM_CALATMOSPHERE next.")
          if (os.path.exists("%s/ASDM_CALATMOSPHERE" % vis)):
              pwvtime, antenna, pwv = readPWVFromASDM_CALATMOSPHERE(vis)
              success = True
              if (len(pwv) < 1):
                  print("Found no data in ASDM_CALATMOSPHERE table")
                  return(0,-1)
          else:
              if (verbose):
                  print("Did not find ASDM_CALATMOSPHERE in the ms")
    except:
        if (verbose):
            print("Could not open ASDM_CALWVR table in the ms")
    finally:
     # try to find the ASDM table
     if (success == False):
       if (len(asdm) > 0):
           if (os.path.exists(asdm) == False):
               print("Could not open ASDM = %s" % (asdm))
               mytb.done()
               return(0,-1)
           try:
               [pwvtime,pwv,antenna] = readpwv(asdm)
           except:
               if (verbose):
                   print("Could not open ASDM = %s" % (asdm))
               mytb.done()
               return(pwvmean,-1)
       else:
           try:
               tryasdm = vis.split('.ms')[0]
               if (verbose):
                   print("No ASDM name provided, so I will try this name = %s" % (tryasdm))
               [pwvtime,pwv,antenna] = readpwv(tryasdm)
           except:
               try:
                   if (verbose):
                       print("Still did not find it.  Will look for CalWVR.xml in current directory.")
                   [pwvtime, pwv, antenna] = readpwv('.')
               except:
                   try:
                       if (verbose):
                           print("Still did not find it.  Will look for CalWVR.xml in the .ms directory.")
                       [pwvtime, pwv, antenna] = readpwv('%s/'%vis)
                   except:
                       if (verbose):
                           print("No CalWVR.xml file found, so no PWV retrieved. Copy it to this directory and try again.")
                       mytb.done()
                       return(pwvmean,-1)
    try:
        matches = np.where(np.array(pwvtime)>myTimes[0])[0]
    except:
        print("Found no times > %d" % (myTimes[0]))
        mytb.done()
        return(0,-1)
    if (len(pwv) < 1):
        print("Found no PWV data")
        return(0,-1)
    ptime = np.array(pwvtime)[matches]
    matchedpwv = np.array(pwv)[matches]
    matches2 = np.where(ptime<myTimes[-1])[0]
    if (len(matches2) < 1):
        # look for the value with the closest start time
        mindiff = 1e12
        for i in range(len(pwvtime)):
            if (abs(myTimes[0]-pwvtime[i]) < mindiff):
                mindiff = abs(myTimes[0]-pwvtime[i])
                pwvmean = pwv[i]*1000
        matchedpwv = []
        for i in range(len(pwvtime)):
            if (abs(abs(myTimes[0]-pwvtime[i]) - mindiff) < 1.0):
                matchedpwv.append(pwv[i])
        pwvmean = 1000*np.median(matchedpwv)
        if (verbose):
            print("Taking the median of %d pwv measurements from all antennas = %.3f mm" % (len(matchedpwv),pwvmean))
        pwvstd = np.std(matchedpwv)
    else:
        pwvmean = 1000*np.median(matchedpwv[matches2])
        pwvstd = np.std(matchedpwv[matches2])
        if (verbose):
            print("Taking the median of %d pwv measurements from all antennas = %.3f mm" % (len(matches2),pwvmean))
#    mytb.done()
    return(pwvmean,pwvstd)
# end of getMedianPWV

def computeAzElFromRADecMJD(raDec, mjd, observatory='ALMA'):
    """
    Computes the az/el for a specified J2000 RA/Dec, MJD and observatory.

    raDec must be in radians: [ra,dec]
    mjd must be in days
    returns the [az,el] in radians
    - Todd Hunter
    """
    myme = measures()
    myqa = quanta()
    mydir = myme.direction('J2000', myqa.quantity(raDec[0],'rad'), myqa.quantity(raDec[1],'rad'))
    myme.doframe(myme.epoch('mjd', myqa.quantity(mjd, 'd')))
    myme.doframe(myme.observatory(observatory))
    myazel = myme.measure(mydir,'azel')
    myqa.done()
    myme.done()
    return([myazel['m0']['value'], myazel['m1']['value']])

def getRADecForField(msName, myfieldId, debug):
    """
    Returns RA,Dec in radians for the specified field in the specified ms.
    -- Todd Hunter
    """
    myms = ms()
    myms.open(msName)
    myd = myms.getfielddirmeas('DELAY_DIR', fieldid=myfieldId)  # dircolname defaults to 'PHASE_DIR'
    myms.close()
    mydir = np.array([[myd['m0']['value']], [myd['m1']['value']]])  # simulates tb.getcell
    return(mydir)

def findClosestTime(mytimes, mytime):
    myindex = 0
    mysep = np.abs(mytimes[0]-mytime)
    for m in range(1,len(mytimes)):
        if (np.abs(mytimes[m] - mytime) < mysep):
            mysep = np.abs(mytimes[m] - mytime)
            myindex = m
    return(myindex)

def getWeather(vis='', scan='', antenna='0',verbose=False, mymsmd=None):
    """
    Queries the WEATHER and ANTENNA tables of an .ms by scan number or
    list of scan numbers in order to return median values of: angleToSun,
      pressure, temperature, humidity, dew point, wind speed, wind direction,
      azimuth, elevation, solarangle, solarelev, solarazim.
    If the sun is below the horizon, the solarangle returned is negated.
    -- Todd Hunter
    """
    if (verbose):
        print("Entered getWeather with vis,scan,antenna = %s,%s,%s" % (str(vis), str(scan), str(antenna)))
    try:
        if str(antenna).isdigit():
            antennaName = mymsmd.antennanames(antenna)[0]
        else:
            antennaName = antenna
            try:
                antenna = mymsmd.antennaids(antennaName)[0]
            except:
                antennaName = string.upper(antenna)
                antenna = mymsmd.antennaids(antennaName)[0]
    except:
        print("Either the ANTENNA table does not exist or antenna %s does not exist" % (antenna))
        return([0,[]])
    mytb = table()
    try:
        mytb.open("%s/POINTING" % vis)
    except:
        print("POINTING table does not exist")
        mytb.done()
        return([0,0])
    subtable = mytb.query("ANTENNA_ID == %s" % antenna)
    mytb.close()
    try:
        mytb.open("%s/OBSERVATION" % vis)
        observatory = mytb.getcell("TELESCOPE_NAME",0)
        mytb.close()
    except:
        print("OBSERVATION table does not exist, assuming observatory == ALMA")
        observatory = "ALMA"
    if (scan == ''):
        scan = mymsmd.scannumbers()
    conditions = {}
    conditions['pressure']=conditions['temperature']=conditions['humidity']=conditions['dewpoint']=conditions['windspeed']=conditions['winddirection'] = 0
    conditions['scan'] = scan
    if (type(scan) == str):
        if (scan.find('~')>0):
            tokens = scan.split('~')
            scan = [int(k) for k in range(int(tokens[0]),int(tokens[1])+1)]
        else:
            scan = [int(k) for k in scan.split(',')]
    if (type(scan) == type(np.ndarray(0))):
        scan = list(scan)
    if (type(scan) == list):
        myTimes = np.array([])
        for sc in scan:
            try:
                print("calling timesforscan")
                newTimes = mymsmd.timesforscan(sc)
                print("times = %s" % (str(newTimes)))
            except:
                print("Error reading scan %d, is it in the data?" % (sc))
                mytb.done()
                return([conditions,[]])
            myTimes = np.concatenate((myTimes,newTimes))
    elif (scan != None):
        try:
            myTimes = mymsmd.timesforscan(scan)
        except:
            print("Error reading scan %d, is it in the data?" % (scan))
            mytb.done()
            return([conditions,[]])
    else:
        mytb.done()
        return([conditions,[]])
    if (type(scan) == str):
        scan = [int(k) for k in scan.split(',')]
    if (type(scan) == list):
        listscan = ""
        listfield = []
        for sc in scan:
#            print("Processing scan ", sc)
            listfield.append(mymsmd.fieldsforscan(sc))
            listscan += "%d" % sc
            if (sc != scan[-1]):
                listscan += ","
#        print("listfield = ", listfield)
        listfields = np.unique(listfield[0])
        listfield = ""
        for field in listfields:
            listfield += "%s" % field
            if (field != listfields[-1]):
                listfield += ","
    else:
        listscan = str(scan)
        listfield = mymsmd.fieldsforscan(scan)
    [az,el] = ComputeSolarAzElForObservatory(myTimes[0], mymsmd)
    [az2,el2] = ComputeSolarAzElForObservatory(myTimes[-1], mymsmd)
    azsun = np.median([az,az2])
    elsun = np.median([el,el2])
    direction = subtable.getcol("DIRECTION")
    azeltime = subtable.getcol("TIME")
    subtable.close()
    telescopeName = mymsmd.observatorynames()[0]
    if (len(direction) > 0 and telescopeName.find('VLA') < 0 and telescopeName.find('NRO') < 0):
      azimuth = direction[0][0]*180.0/math.pi  # a list of values
      elevation = direction[1][0]*180.0/math.pi # a list of values
      npat = np.array(azeltime)
      matches = np.where(npat>myTimes[0])[0]
      matches2 = np.where(npat<myTimes[-1])[0]
      if (len(matches2) > 0 and len(matches) > 0):
          if verbose: print("matches[0]=%d, matches2[-1]=%d" % (matches[0],matches[-1]))
          matchingIndices = list(range(matches[0],matches2[-1]+1))
      else:
          matchingIndices = []
      if (len(matchingIndices) > 0):  # CAS-8440
          conditions['azimuth'] = np.median(azimuth[matches[0]:matches2[-1]+1])
          conditions['elevation'] = np.median(elevation[matches[0]:matches2[-1]+1])
      elif (len(matches) > 0):        # CAS-8440
          if verbose: print("using median of all az/el values after time 0")
          conditions['azimuth'] = np.median(azimuth[matches[0]])
          conditions['elevation'] = np.median(elevation[matches[0]])
      else:                           # CAS-8440
          if verbose: print("using median of all az/el values")
          conditions['azimuth'] = np.median(azimuth)
          conditions['elevation'] = np.median(elevation)
      conditions['solarangle'] = angularSeparation(azsun,elsun,conditions['azimuth'],conditions['elevation'])
      conditions['solarelev'] = elsun
      conditions['solarazim'] = azsun
      if (verbose):
          print("Using antenna = %s to retrieve median azimuth and elevation" % (antennaName))
          print("Separation from sun = %f deg" % (abs(conditions['solarangle'])))
      if (elsun<0):
        conditions['solarangle'] = -conditions['solarangle']
        if (verbose):
            print("Sun is below horizon (elev=%.1f deg)" % (elsun))
      else:
        if (verbose):
            print("Sun is above horizon (elev=%.1f deg)" % (elsun))
      if (verbose):
          print("Average azimuth = %.2f, elevation = %.2f degrees" % (conditions['azimuth'],conditions['elevation']))
    else:
      if (verbose): print("The POINTING table is either blank or does not contain Azim/Elev.")
      if (type(scan) == int or type(scan)==np.int32):
          # compute Az/El for this scan
        myfieldId = mymsmd.fieldsforscan(scan)
        if (type(myfieldId) == list or type(myfieldId) == type(np.ndarray(0))):
            myfieldId = myfieldId[0]
        fieldName = mymsmd.namesforfields(myfieldId)
        if (type(fieldName) == list or type(fieldName) == type(np.ndarray(0))):
            fieldName = fieldName[0]
#        print("A) fieldname = ", fieldName)
#        print("myfieldId = ", myfieldId)
        myscantime = np.median(mymsmd.timesforscan(scan))
#        print("Calling getRADecForField")
        mydirection = getRADecForField(vis, myfieldId, verbose)
        if (verbose): print("mydirection= %s" % (str(mydirection)))
        if (len(telescopeName) < 1):
            telescopeName = 'ALMA'
        myazel = computeAzElFromRADecMJD(mydirection, myscantime/86400., telescopeName)
        conditions['elevation'] = myazel[1] * 180/math.pi
        conditions['azimuth'] = myazel[0] * 180/math.pi
        conditions['solarangle'] = angularSeparation(azsun,elsun,conditions['azimuth'],conditions['elevation'])
        conditions['solarelev'] = elsun
        conditions['solarazim'] = azsun
        if (verbose):
            print("Separation from sun = %f deg" % (abs(conditions['solarangle'])))
        if (elsun<0):
            conditions['solarangle'] = -conditions['solarangle']
            if (verbose):
                print("Sun is below horizon (elev=%.1f deg)" % (elsun))
        else:
            if (verbose):
                print("Sun is above horizon (elev=%.1f deg)" % (elsun))
        if (verbose):
            print("Average azimuth = %.2f, elevation = %.2f degrees" % (conditions['azimuth'],conditions['elevation']))
      elif (type(scan) == list):
          myaz = []
          myel = []
          if (verbose):
              print("Scans to loop over = %s" % (str(scan)))
          for s in scan:
              fieldName = mymsmd.fieldsforscan(s)
              if (type(fieldName) == list):
                  # take only the first pointing in the mosaic
                  fieldName = fieldName[0]
              myfieldId = mymsmd.fieldsforname(fieldName)
              if (type(myfieldId) == list or type(myfieldId)==type(np.ndarray(0))):
                  # If the same field name has two IDs (this happens in EVLA data)
                  myfieldId = myfieldId[0]
              myscantime = np.median(mymsmd.timesforscan(s))
              mydirection = getRADecForField(vis, myfieldId, verbose)
              telescopeName = mymsmd.observatorynames()[0]
              if (len(telescopeName) < 1):
                  telescopeName = 'ALMA'
              myazel = computeAzElFromRADecMJD(mydirection, myscantime/86400., telescopeName)
              myaz.append(myazel[0]*180/math.pi)
              myel.append(myazel[1]*180/math.pi)
          conditions['azimuth'] = np.median(myaz)
          conditions['elevation'] = np.median(myel)
          conditions['solarangle'] = angularSeparation(azsun,elsun,conditions['azimuth'],conditions['elevation'])
          conditions['solarelev'] = elsun
          conditions['solarazim'] = azsun
          if (verbose):
              print("Using antenna = %s to retrieve median azimuth and elevation" % (antennaName))
              print("Separation from sun = %f deg" % (abs(conditions['solarangle'])))
          if (elsun<0):
              conditions['solarangle'] = -conditions['solarangle']
              if (verbose):
                  print("Sun is below horizon (elev=%.1f deg)" % (elsun))
          else:
              if (verbose):
                  print("Sun is above horizon (elev=%.1f deg)" % (elsun))
          if (verbose):
              print("Average azimuth = %.2f, elevation = %.2f degrees" % (conditions['azimuth'],conditions['elevation']))


    # now, get the weather
    if not os.path.exists('%s/WEATHER' % vis):
        print("There is no WEATHER table for this ms.")
        if (needToClose_mymsmd): mymsmd.close()
        return([conditions,myTimes])
    try:
        mytb.open("%s/WEATHER" % vis)
    except:
        print("Could not open the WEATHER table for this ms.")
        mytb.done()
        return([conditions,myTimes])
    if (True):
        mjdsec = mytb.getcol('TIME')
        indices = np.argsort(mjdsec)
        mjd = mjdsec/86400.
        pressure = mytb.getcol('PRESSURE')
        conditions['pressure_unit'] = mytb.getcolkeywords('PRESSURE').get('QuantumUnits', ['mbar'])[0]
        relativeHumidity = mytb.getcol('REL_HUMIDITY')
        temperature = mytb.getcol('TEMPERATURE')
        if (np.median(temperature) > 100):
            # must be in units of Kelvin, so convert to C
            temperature -= 273.15
        if 'DEW_POINT' in mytb.colnames():
            dewPoint = mytb.getcol('DEW_POINT')
            if (np.median(dewPoint) > 100):
                # must be in units of Kelvin, so convert to C
                dewPoint -= 273.15
            if (np.median(dewPoint) == 0):
                # assume it is not measured and use NOAA formula to compute from humidity:
                dewPoint = ComputeDewPointCFromRHAndTempC(relativeHumidity, temperature)
        else:
            dewPoint = None  # Nobeyama measurement sets do not have a dewpoint column
        sinWindDirection = np.sin(mytb.getcol('WIND_DIRECTION'))
        cosWindDirection = np.cos(mytb.getcol('WIND_DIRECTION'))
        windSpeed = mytb.getcol('WIND_SPEED')
        mytb.done()

        # put values into time order (they mostly are, but there can be small differences)
        mjdsec = np.array(mjdsec)[indices]
        pressure = np.array(pressure)[indices]
        relativeHumidity = np.array(relativeHumidity)[indices]
        temperature = np.array(temperature)[indices]
        if dewPoint is not None:
            dewPoint = np.array(dewPoint)[indices]
        windSpeed = np.array(windSpeed)[indices]
        sinWindDirection = np.array(sinWindDirection)[indices]
        cosWindDirection = np.array(cosWindDirection)[indices]

        # find the overlap of weather measurement times and scan times
        matches = np.where(mjdsec>=np.min(myTimes))[0]
        matches2 = np.where(mjdsec<=np.max(myTimes))[0]
#            print("len(matches)=%d, len(matches2)=%d" % (len(matches), len(matches2)))
        noWeatherData = False
        if (len(matches)>0 and len(matches2) > 0):
            # average the weather points enclosed by the scan time range
            selectedValues = list(range(matches[0], matches2[-1]+1))
            if (selectedValues == []):
                # there was a either gap in the weather data, or an incredibly short scan duration
                if (verbose):
                    print("----  Finding the nearest weather value --------------------------- ")
                selectedValues = findClosestTime(mjdsec, myTimes[0])
        elif (len(matches)>0):
            # all points are greater than myTime, so take the first one
            selectedValues = matches[0]
        elif (len(matches2)>0):
            # all points are less than myTime, so take the last one
            selectedValues = matches2[-1]
        else:
            # table has no weather data!
            noWeatherData = True
        if (noWeatherData):
            conditions['pressure'] = 563.0
            conditions['temperature'] = 0  # Celsius is expected
            conditions['humidity'] = 20.0
            conditions['dewpoint'] = -20.0
            conditions['windspeed'] = 0
            conditions['winddirection'] = 0
            print("WARNING: No weather data found in the WEATHER table!")
        else:
          if (type(selectedValues) == np.int64 or type(selectedValues) == np.int32 or
              type(selectedValues) == int):
              conditions['readings'] = 1
              if (verbose):
                  print("selectedValues=%d, myTimes[0]=%.0f, len(matches)=%d, len(matches2)=%d" % (selectedValues,
                     myTimes[0], len(matches), len(matches2)))
                  if (len(matches) > 0):
                      print("matches[0]=%f, matches[-1]=%f" % (matches[0], matches[-1]))
                  if (len(matches2) > 0):
                      print("matches2[0]=%f, matches2[-1]=%d" % (matches2[0], matches2[-1]))
          else:
              conditions['readings'] = len(selectedValues)
          conditions['pressure'] = np.median(pressure[selectedValues])
          if (conditions['pressure'] != conditions['pressure']):
              # A nan value got through, due to no selected values (should be impossible)"
              if (verbose):
                  print(">>>>>>>>>>>>>>>>>>>>>>>>  selectedValues = %s" % (str(selectedValues)))
                  print("len(matches)=%d, len(matches2)=%d" % (len(matches), len(matches2)))
                  print("matches[0]=%f, matches[-1]=%f, matches2[0]=%f, matches2[-1]=%d" % (matches[0], matches[-1], matches2[0], matches2[-1]))
          conditions['temperature'] = np.median(temperature[selectedValues])
          conditions['humidity'] = np.median(relativeHumidity[selectedValues])
          if dewPoint is not None:
              conditions['dewpoint'] = np.nanmedian(dewPoint[selectedValues])
          conditions['windspeed'] = np.median(windSpeed[selectedValues])
          conditions['winddirection'] = (180./math.pi)*np.arctan2(np.median(sinWindDirection[selectedValues]),np.median(cosWindDirection[selectedValues]))
          if (conditions['winddirection'] < 0):
              conditions['winddirection'] += 360
          if (verbose):
              print("Median weather values for scan %s (field %s)" % (listscan,listfield))
              print("  Pressure = %.2f mb" % (conditions['pressure']))
              print("  Temperature = %.2f C" % (conditions['temperature']))
              if dewPoint is not None:
                  print("  Dew point = %.2f C" % (conditions['dewpoint']))
              print("  Relative Humidity = %.2f %%" % (conditions['humidity']))
              print("  Wind speed = %.2f m/s" % (conditions['windspeed']))
              print("  Wind direction = %.2f deg" % (conditions['winddirection']))

    return([conditions,myTimes])
    # end of getWeather

def getBasebandNumbers(inputMs) :
    """
    Returns the baseband numbers associated with each spw in the specified ms.
    Todd Hunter
    """
    if (os.path.exists(inputMs) == False):
        print("measurement set not found")
        return -1
    mytb = table()
    mytb.open("%s/SPECTRAL_WINDOW" % inputMs)
    if ("BBC_NO" in mytb.colnames()):
        bbNums = mytb.getcol("BBC_NO")
    else:
        return(-1)
    mytb.close()
    return bbNums

def yigHarmonic(bandString):
    """
    Returns the YIG harmonic for the specified ALMA band, given as a string
    used in casa tables.
    For example:  yigHarmonic('ALMA_RB_03')  returns the integer 6.
    Todd Hunter
    """
    # remove any leading spaces
    #bandString = bandString[bandString.find('ALMA_RB'):]
    harmonics = {'ALMA_RB_03':6, 'ALMA_RB_04':6, 'ALMA_RB_06': 18,
                 'ALMA_RB_07': 18, 'ALMA_RB_08':18, 'ALMA_RB_09':27}
    try:
        harmonic = harmonics[bandString]
    except:
        harmonic = -1
    return(harmonic)

def interpretLOs(vis, parentms='', showWVR=False,
                 showCentralFreq=False, verbose=False, show=False,
                 alsoReturnLO2=False, showChannelAverageSpws=False,
                 showOnlyScienceSpws=False, birdieFreq=None, birdieSpw=None,
                 intent='OBSERVE_TARGET#ON_SOURCE', spwsForIntent=None,
                 showEffective=False, showWindowFactors=False, mymsmd=None):
    """
    Copied from analysisUtils on May 16, 2017, to replace old version, in order
    to fix SCOPS-4877.
    Interpret (and optionally print) the LO settings for an MS from the
    ASDM_RECEIVER table.
    Options:
    showCentralFreq: if True, then show the mean frequency of each spw,
                     otherwise show the frequency of the first channel
    showWVR: include the WVR spw in the list
    parentms:  if the dataset has been split from a parent dataset, then
               you may also need to specify the name of the parent ms.
    alsoReturnLO2: if True, return a second dictionary of the LO2 values
    birdieFreq: if specified, compute the IF of this RF feature
    birdieSpw: only necessary if more than one LO1 in the science spws
    intent: which intent to use in spwsforintent (to find science spws)
    spwsForIntent: if specified, then avoid the call to spwsforintent

    Returns: a dictionary of the LO1 values (in Hz) for each spw, keyed by
             integer.

    A typical band 7 TDM dataset (prior to splitting) looks like this:
    SPECTRAL_WINDOW table has 39 rows:   row
           WVR                           0
           8 band 3 windows (pointing)   1-8
           8 band 7 windows              9-16
           22 WVR windows                17-38
    The corresponding ASDM_RECEIVER table has only 18 rows:
           WVR                           0
           8 band 3 windows              1-8
           WVR                           9
           8 band 7 windows              10-17
    After splitting, the ASDM_RECEIVER table remains the same, but the
    SPECTRAL WINDOW table then has only 4 rows, as the pointing spws and
    the channel-averaged data are dropped:
           4 band 7 windows

    Todd Hunter
    """
    lo1s = {} # initialize dictionary to be returned
    lo2s = {}
    try:
        retval =  getLOs(vis)
        [LOs,bands,spws,names,sidebands,receiverIds,spwNames] = retval
    except:
        print("getLOs failed")
        return(retval)
    if (verbose): print("len(spws) = %d: %s" % (len(spws), str(spws)))
    maxSpw = np.max(spws)
    sawWVR = False
    indices = []  # will exclude the extraneous WVR spws
    for i in range(len(spws)):
        if (names[i].find('WVR') >= 0):
            if (not sawWVR):
                indices.append(i)
                sawWVR = True
        else:
            indices.append(i)
    LOs = np.array(LOs, dtype=object)[indices]
    bands = np.array(bands, dtype=object)[indices]
    spws = list(np.array(spws, dtype=object)[indices])
    names = np.array(names, dtype=object)[indices]
    sidebands = np.array(sidebands, dtype=object)[indices]
    receiverIds = np.array(receiverIds, dtype=object)[indices]
    index = list(range(len(spws)))
    mytb = table()
    mytb.open(vis+'/SPECTRAL_WINDOW')
    # If the data have been split into an ms with fewer spws, then this
    # table will be smaller (in rows) than the parent MS's table.
    spwNames = mytb.getcol('NAME')
    mytb.close()
    splitted = False
    if (maxSpw != len(spwNames)-1):
        splitted = True
        if (verbose):
            print("maxSpw=%d != len(spwNames)=%d)" % (maxSpw, len(spwNames)))
        if (parentms == '' or parentms == None):
            print("You appear to have split these data.  Please provide the parentms as an argument.")
            return
        mytb.open(parentms+'/SPECTRAL_WINDOW')
        parentSpwNames = mytb.getcol('NAME')
        mytb.close()
        extractedRows = []
        index = []
        for s in range(len(spwNames)):
            if (len(spwNames[s]) == 0):
                print("This is an old dataset lacking values in the NAME column of the SPECTRAL_WINDOW table.")
                return
            if (verbose):
                print("Checking for %s in " % (spwNames[s]), parentSpwNames)
            extractedRows.append(np.where(parentSpwNames == spwNames[s])[0][0])
            index.append(spws.index(extractedRows[-1]))
            if (verbose):
                print("spw %d came from spw %d" % (s, extractedRows[-1]))
# extractedRows = the row of the parent SPECTRAL_WINDOW table that matches
#                 the split-out spw
#     index = the row of the ASDM_RECEIVER table that matches the split-out spw
        vis = parentms
    if (verbose):
        print("spwNames = ", spwNames)
        print("spws = ", spws)
        print("bands = ", bands)
        output = "LOs = "
        for LO in LOs:
            output += "%.3f, " % (LO[0]*1e-9)
        print(output)
        print("names = ", names)
        print("index = ", index)

    bbc = getBasebandNumbers(vis) # does not use msmd
    if (show):
        print('Row refers to the row number in the ASDM_RECEIVER table (starting at 0).')
        if (showCentralFreq):
            myline = 'Row spw BB RxBand CenFreq Nchan LO1(GHz) LO2(GHz) Sampler YIG(GHz) TFBoffset(MHz)'
        else:
            myline = 'Row spw BB RxBand Ch1Freq Nchan LO1(GHz) LO2(GHz) Sampler YIG(GHz) TFBoffset(MHz)'
        if (showEffective):
            myline += ' Eff.BW(MHz) Res(MHz) Width(MHz)'
        if (showWindowFactors):
            myline += ' windowFactors'
        print(myline)

    # Loop over all rows in the ASDM_RECEIVER table, unless we've split, in
    # which case this will loop over the N spws in the table.
    needToClose = False
    if mymsmd is None or mymsmd == '':
        mymsmd = msmetadata()
        mymsmd.open(vis)
        needToClose = True
    if (spwsForIntent == None):
        if intent in mymsmd.intents(): # prevent a warning of OBSERVE_TARGET does not exist
            scienceSpws = np.setdiff1d(mymsmd.spwsforintent(intent),mymsmd.wvrspws())
        else:
            scienceSpws = []
    else:
        scienceSpws = spwsForIntent
    birdieIF = 0
    if (birdieFreq is not None):
        birdieFreq = parseFrequencyArgumentToHz(birdieFreq)
        birdieFreqGHz = parseFrequencyArgumentToGHz(birdieFreq)
    fdmSpws = mymsmd.almaspws(fdm=True)
    for i in range(len(index)):
        if (verbose):
            print("index[%d]=%d" % (i,index[i]))
            print("spws[%d] = %d" % (index[i], spws[index[i]]))
        myspw = i
        if (birdieFreq is not None and birdieIF == 0):
            if (myspw == birdieSpw):
                if verbose:
                    print("spw=%d, Computing IF = %f - %f" % (myspw, birdieFreq, LOs[index[i]][0]))
                birdieIF = np.fabs(birdieFreq - LOs[index[i]][0])
            elif (myspw in scienceSpws and birdieSpw==None):
                if verbose:
                    print("spw=%d (in %s), Computing IF = %f - %f" % (myspw, str(scienceSpws), birdieFreq, LOs[index[i]][0]))
                birdieIF = np.fabs(birdieFreq - LOs[index[i]][0])
        freqs = mymsmd.chanfreqs(myspw)
        meanFreqGHz = mymsmd.meanfreq(myspw) * (1e-9)
        if (myspw not in scienceSpws and showOnlyScienceSpws): continue
        if (len(freqs) < 2 and showChannelAverageSpws==False):
            continue
        if (bands[index[i]].split('_')[-1].isdigit()):
            rxband = bands[index[i]].split('_')[-1]
        elif (showWVR):
            rxband = 'WVR'
        else:
            continue
        line = "%2d  %2d  %d %3s " % (spws[index[i]], myspw, bbc[myspw], rxband)
        if (showCentralFreq):
            line += "%10.6f %4d " % (meanFreqGHz,len(freqs))
        else:
            line += "%10.6f %4d " % (freqs[0],len(freqs))

        if (LOs[index[i]][0] < 0):
            print(line)
            continue
        if (bbc[myspw] > 0):
            if (splitted):
                lo1s[i] = LOs[index[i]][0]
                lo2s[i] = LOs[index[i]][1]
            else:
                lo1s[myspw] = LOs[index[i]][0]
                lo2s[myspw] = LOs[index[i]][1]
            for j in range(len(LOs[index[i]])):
                if (j != 2):
                    line = line + '%10.6f' % (LOs[index[i]][j]*1e-9)
                else:
                    line = line + '%5.2f' % (LOs[index[i]][j]*1e-9)
        yig = LOs[index[i]][0] / yigHarmonic(bands[index[i]])
        if (yig > 0):
            line = line + ' %.6f' % (yig*1e-9)
        if (myspw in fdmSpws):
            # work out what LO4 must have been
            LO1 = LOs[index[i]][0]
            LO2 = LOs[index[i]][1]
            LO3 = LOs[index[i]][2]
            if (sidebands[index[i]][0] == 'USB'):
                IFlocation = LO3 - (LO2 - (meanFreqGHz*1e9 - LO1))
            else:
                IFlocation = LO3 - (LO2 - (LO1 -  meanFreqGHz*1e9))
            LO4 = 2e9 + IFlocation
            TFBLOoffset = LO4-3e9
            line += '%9.3f %+8.3f ' % (LO4 * 1e-6,  TFBLOoffset * 1e-6)
        else:
            line += 19*' '
        if (showEffective):
            line += '%9.4f %9.4f %9.4f' % (effectiveBandwidth(vis, myspw)*1e-6,
                                           effectiveResolution(vis, myspw)*1e-6,
                                           getChanWidths(mymsmd, myspw)*1e-6)
        if (showWindowFactors):
            chanwidth = abs(getChanWidths(mymsmd,myspw))
            line += ' %.4f %.4f' % (effectiveBandwidth(vis, myspw)/chanwidth,
                                    effectiveResolution(vis, myspw)/chanwidth)
        if (bands[index[i]].find('ALMA_RB_06')>=0 or bands[index[i]].find('ALMA_RB_09')>=0):
            if (len(LOs[index[i]]) > 1):
                if (LOs[index[i]][1] < 11.3e9 and LOs[index[i]][1] > 10.5e9):
                    line = line + ' leakage of LO2 undesired sideband may degrade dynamic range'
                    if (bands[index[i]].find('ALMA_RB_06')>=0):
                        line += ' (and YIG may leak in)'
                    yigLeakage = LOs[index[i]][0] + (LOs[index[i]][1] - LOs[index[i]][2]) + (yig - LOs[index[i]][1])
                    if (yigLeakage > 0):
                        line = line + ' at %.6f' % (yigLeakage*1e-9)
        if (show): print(line)
    if needToClose:
        mymsmd.done()
    if (birdieIF != 0):
        print("The feature at %f GHz is at IF = %f GHz." % (birdieFreqGHz, birdieIF*1e-9))
    if (alsoReturnLO2):
        return(lo1s, lo2s)
    else:
        return(lo1s)

def mjdSecondsToMJDandUT(mjdsec):
    """
    Converts a value of MJD seconds into MJD, and into a UT date/time string.
    example: (56000.0, '2012-03-14 00:00:00 UT')
    Caveat: only works for a scalar input value
    Todd Hunter
    """
    myme = measures()
    today = myme.epoch('utc','today')
    mjd = np.array(mjdsec) / 86400.
    today['m0']['value'] =  mjd
    myqa = quanta()
    hhmmss = myqa.time(today['m0'], form='', prec=0, showform=False)[0]
#    print("hhmmss = ", hhmmss)
    date = myqa.splitdate(today['m0'])
    myqa.done()
    utstring = "%s-%02d-%02d %s UT" % (date['year'],date['month'],date['monthday'],hhmmss)
    myme.done()
    return(mjd, utstring)

def ComputeSolarAzElForObservatory(mjdsec, mymsmd):
    pos =  mymsmd.observatoryposition()
    longitude = pos['m0']['value'] * 180/np.pi
    latitude = pos['m1']['value'] * 180/np.pi
    return(ComputeSolarAzElLatLong(mjdsec,latitude,longitude))

def ComputeSolarAzElLatLong(mjdsec,latitude,longitude):
    """
    Computes the apparent Az,El of the Sun for a specified time and location
    on Earth.  Latitude and longitude must arrive in degrees, with positive
    longitude meaning east of Greenwich.
    -- Todd Hunter
    """
    DEG_TO_RAD = math.pi/180.
    RAD_TO_DEG = 180/math.pi
    HRS_TO_RAD = math.pi/12.
    [RA,Dec] = ComputeSolarRADec(mjdsec)
    LST = ComputeLST(mjdsec, longitude)

    phi = latitude*DEG_TO_RAD
    hourAngle = HRS_TO_RAD*(LST - RA)
    azimuth = RAD_TO_DEG*math.atan2(math.sin(hourAngle), (math.cos(hourAngle)*math.sin(phi) - math.tan(Dec*DEG_TO_RAD)*math.cos(phi)))

    # the following is to convert from South=0 (which the French formula uses)
    # to North=0, which is what the rest of the world uses */
    azimuth += 180.0;

    if (azimuth > 360.0):
        azimuth -= 360.0
    if (azimuth < 0.0):
        azimuth += 360.0

    argument = math.sin(phi)*math.sin(Dec*DEG_TO_RAD) + math.cos(phi)*math.cos(Dec*DEG_TO_RAD) * math.cos(hourAngle);
    elevation = RAD_TO_DEG*math.asin(argument);
    return([azimuth,elevation])

def ComputeSolarRADec(mjdsec):
    """
    Computes the RA,Dec of the Sun for a specified time. -- Todd Hunter
    """
    jd = mjdToJD(mjdsec/86400.)
    RAD_TO_DEG = 180/math.pi
    RAD_TO_HRS = (1.0/0.2617993877991509)
    DEG_TO_RAD = math.pi/180.
    T = (jd - 2451545.0) / 36525.0
    Lo = 280.46646 + 36000.76983*T + 0.0003032*T*T
    M = 357.52911 + 35999.05029*T - 0.0001537*T*T
    Mrad = M * DEG_TO_RAD
    e = 0.016708634 - 0.000042037*T - 0.0000001267*T*T
    C = (1.914602 - 0.004817*T - 0.000014*T*T) * math.sin(Mrad) +  (0.019993 - 0.000101*T) * math.sin(2*Mrad) + 0.000289*math.sin(3*Mrad)
    L = Lo + C
    nu = DEG_TO_RAD*(M + C)
    R = 1.000001018 * (1-e*e) / (1 + e*math.cos(nu))
    Omega = DEG_TO_RAD*(125.04 - 1934.136*T)
    mylambda = DEG_TO_RAD*(L - 0.00569 - 0.00478 * math.sin(Omega))
    epsilon0 = (84381.448 - 46.8150*T - 0.00059*T*T + 0.001813*T*T*T) / 3600.
    epsilon = (epsilon0 + 0.00256 * math.cos(Omega)) * DEG_TO_RAD
    rightAscension = RAD_TO_HRS*math.atan2(math.cos(epsilon)*math.sin(mylambda), math.cos(mylambda))
    if (rightAscension < 0):
        rightAscension += 24.0
    argument = math.sin(epsilon) * math.sin(mylambda)
    declination = RAD_TO_DEG*math.asin(argument)
    return([rightAscension, declination])

def angularSeparation(ra0,dec0,ra1,dec1, returnComponents=False):
    """
    Usage:  angularSeparation(ra0,dec0,ra1,dec1)
    Computes the great circle angle between two celestial coordinates.
    using the Vincenty formula (from wikipedia) which is correct for all
    angles, as long as you use atan2() to handle a zero denominator.
    See  http://en.wikipedia.org/wiki/Great_circle_distance
    ra,dec must be given in degrees, as is the output.
    It also works for the az,el coordinate system.
    Comopnent separations are field_0 minus field_1.
    See also angularSeparationRadians()
    -- Todd Hunter
    """
    ra0 *= math.pi/180.
    dec0 *= math.pi/180.
    ra1 *= math.pi/180.
    dec1 *= math.pi/180.
    deltaLong = ra0-ra1
    argument1 = (((math.cos(dec1)*math.sin(deltaLong))**2) +
                 ((math.cos(dec0)*math.sin(dec1)-math.sin(dec0)*math.cos(dec1)*math.cos(deltaLong))**2))**0.5
    argument2 = math.sin(dec0)*math.sin(dec1) + math.cos(dec0)*math.cos(dec1)*math.cos(deltaLong)
    angle = math.atan2(argument1, argument2) / (math.pi/180.)
    if (returnComponents):
        radegrees = (ra0-ra1)*180/math.pi
        decdegrees = (dec0-dec1)*180/math.pi
        retval = angle,radegrees,decdegrees
    else:
        retval = angle
    return(retval)

def ComputeDewPointCFromRHAndTempC(relativeHumidity, temperature):
    """
    inputs: relativeHumidity in percentage, temperature in C
    output: in degrees C
    Uses formula from http://en.wikipedia.org/wiki/Dew_point#Calculating_the_dew_point
    Todd Hunter
    """
    temperature = np.array(temperature)  # protect against it being a list
    relativeHumidity = np.array(relativeHumidity)  # protect against it being a list
    es = 6.112 * np.exp(17.67 * temperature / (temperature + 243.5))
    E = relativeHumidity * 0.01 * es
    # patch problematic cases where relativy humiditiy is -1
    # requires changing numpy.mean to numpy.nanmean downstream the code to avoid bad averages of the dewpoint
    dewPoint = np.zeros(len(E))
    for i in range(len(E)):
        if E[i] <= 0:
            dewPoint[i] = None
        else:
            dewPoint[i] = 243.5 * np.log(E[i] / 6.112) / (17.67 - np.log(E[i] / 6.112))

    return dewPoint

def ComputeLST(mjdsec, longitude):
    """
    Computes the LST (in hours) for a specified time and longitude.
    The input longitude is in degrees, where east of Greenwich is positive.
    -- Todd Hunter
    """
    JD = mjdToJD(mjdsec/86400.)
    T = (JD - 2451545.0) / 36525.0
    sidereal = 280.46061837 + 360.98564736629*(JD - 2451545.0) + 0.000387933*T*T - T*T*T/38710000.
    # now we have LST in Greenwich, need to scale back to site
    sidereal += longitude
    sidereal /= 360.
    sidereal -= np.floor(sidereal)
    sidereal *= 24.0
    if (sidereal < 0):
        sidereal += 24
    if (sidereal >= 24):
        sidereal -= 24
    return(sidereal)

def mjdToJD(MJD):
    """
    Converts an MJD value to JD
    """
    JD = MJD + 2400000.5
    return(JD)

def splitListIntoContiguousLists(mylist):
    """
    Converts [1,2,3,5,6,7] into [[1,2,3],[5,6,7]], etc.
    -Todd Hunter
    """
    mylists = []
    newlist = [mylist[0]]
    for i in range(1,len(mylist)):
        if (mylist[i-1] != mylist[i]-1):
            mylists.append(newlist)
            newlist = [mylist[i]]
        else:
            newlist.append(mylist[i])
    mylists.append(newlist)
    return(mylists)

# Removed for CAS-8065
#def getScansForTimes(mymsmd, scantimes):
#    myscans = []
#    myscantimes = []
##    print("len(scantimes) = ", len(scantimes))
#    scantimes = splitListIntoContiguousLists(scantimes)
#    for t in scantimes:
#        mean_t = np.mean(t)
#        range_t = (1+np.max(t)-np.min(t))*0.5
#        scans_t = mymsmd.scansfortimes(mean_t, range_t)
#        if (len(scans_t) > 0):
#            scan = scans_t[0]
#            #        print("scansfortime(%f) = " % (t), scan)
#            myscans.append(scan)
#            myscantimes.append(t)
#    return(myscans, myscantimes)

def pruneFilelist(filelist):
    """
    Reduce size of filenames in filelist to the extent that current working directory
    agrees with the path.
    """
    mypwd = os.getcwd() + '/'
    newfilelist = []
    for f in filelist:
        fstart = 0
        if (f.find(mypwd) == 0):
            fstart = len(mypwd)
        newfilelist.append(f[fstart:])
    return(newfilelist)