Пример #1
0
  def plotSpec(self, filename=None, plotother=False):

    """
       Determine the current state of the widget entries and plot the
       spectrum accordingly.
    """

    # Should I use the default name?
    if plotother:
      # No - put filename to None
      filename = None
    else:
      # Yes - read it from the entry
      filename = self.lastplotname


    # Now, if no filename is defined yet, open a dialog to ask for a file to be
    # plotted
    if not filename:
      filename = tkFileDialog.askopenfilename(parent=self, initialdir=config.options["outpath"].value)
      if filename: self.lastplotname = filename
      else: return


#   Not yet implemented :
#   LOOP OVER RANGES - WHEN THERE ARE SEVERAL RANGES DEFINED - DO WE WANT THIS?


    # Check that the file to plot (still) exists and then ask the task manager
    # to execute the 'plotspec' task.
    if os.path.exists(filename):
      taskManager.runTask(tasks.plotspec, filename, usebiggles=self.usebiggles.get())
    else:
      messageLog.put_warning('Could not open file for plotting - Skipped (%s)' % filename)
Пример #2
0
    def plotSpec(self, filename=None, plotother=False):
        """
       Determine the current state of the widget entries and plot the
       spectrum accordingly.
    """

        # Should I use the default name?
        if plotother:
            # No - put filename to None
            filename = None
        else:
            # Yes - read it from the entry
            filename = self.lastplotname

        # Now, if no filename is defined yet, open a dialog to ask for a file to be
        # plotted
        if not filename:
            filename = tkFileDialog.askopenfilename(
                parent=self, initialdir=config.options["outpath"].value)
            if filename: self.lastplotname = filename
            else: return

#   Not yet implemented :
#   LOOP OVER RANGES - WHEN THERE ARE SEVERAL RANGES DEFINED - DO WE WANT THIS?

# Check that the file to plot (still) exists and then ask the task manager
# to execute the 'plotspec' task.
        if os.path.exists(filename):
            taskManager.runTask(tasks.plotspec,
                                filename,
                                usebiggles=self.usebiggles.get())
        else:
            messageLog.put_warning(
                'Could not open file for plotting - Skipped (%s)' % filename)
Пример #3
0
def getwavedef1D(frame):


  """
     Extract and return the wavelength definition vector [ordernumber, zero
     wavelength offset, wavelength step] using the FITS header keywords of a
     1-dimensional spectrum.
  """

  # Read and store the appropriate headers
  try:
    npixels  = frame[0].header['NAXIS1']
    zerowave = frame[0].header['CRVAL1']
    refpix   = frame[0].header['CRPIX1']
    wstep    = frame[0].header['CDELT1']
  except KeyError:
    # Error when reading headers!
    messageLog.put_warning('Could not find FITS headers defining the wavelength solution')
    return None

  # Determine starting (reference) wavelength
  woffset = zerowave + (refpix - 1.0) * wstep

  # And create the wavelength vector
  wavearray   = numpy.zeros((1, 4), dtype=numpy.float64)
  wavearray[0, :]   = [0, 0, woffset, wstep]

  # Return the vector to the caller
  return wavearray
Пример #4
0
def helcorr(frame):

  # NB: This routine assumes that the file containes the 'DATE-AVG' keyword,
  #     which is currently only added by the MEF post-processing scripts
  #
  #     To be checked : which epoch are RA and DEC given in, and which role
  #     does EQUINOX play in this?

  """
     Determine the heliocentric velocity correction from the frame headers
  """

  # Read the appropriate headers
  try:
    dateavg  = frame[0].header['DATE-AVG']
  except KeyError:
    # Error when reading headers!
    messageLog.put_warning('Could not find DATE-AVG header: skipping heliocentric velocity calculation')
    return

  try:
    ra       = float(frame[0].header['RA'])
    dec      = float(frame[0].header['DEC'])
    epoch    = float(frame[0].header['EQUINOX'])
  except KeyError:
    # Error when reading headers!
    messageLog.put_warning('Could not read object coordinates: skipping heliocentric velocity calculation')
    return

  # Example of header card :
  # DATE-AVG= '2003-12-13T16:52:12.0' / Midpoint of observation

  # Extract useful data from header
  year  = int(dateavg[0:4])
  month = int(dateavg[5:7])
  day   = int(dateavg[8:10])
  ut    = dateavg[11:]

  # Obtain the heliocentric correction from IRAF
  vhelio = irafWrappers.rvcorrect(year, month, day, ut, ra / 15.0, dec, epoch)

  # Write result to frame header
  frame[0].header.update('VHELIO', vhelio)
  frame[0].header.add_history('Added header card for heliocentric velocity')
  messageLog.put('Added header card VHELIO with heliocentric velocity', 5)
Пример #5
0
def fixheaders(frame):

  # Make sure header cards GAIN and RDNOISE are defined
  # Gain = 1.0 and RDNOISE = 0.0 are the only feasible assumptions one can
  # make if nothing else is known.

  data = pyfits.open(frame, 'update')
  try:
    gain = data[0].header['GAIN']
  except KeyError:
    data[0].header.update('GAIN', 1.0)
    messageLog.put_warning('Could not find GAIN header: assumed GAIN = 1.0')
    data[0].header.add_history('Inserted missing GAIN header card, assumed GAIN = 1.0')
  try:
    rdnoise = data[0].header['RDNOISE']
  except KeyError:
    data[0].header.update('RDNOISE', 0.0)
    messageLog.put_warning('Could not find RDNOISE header: assumed RDNOISE = 0.0')
    data[0].header.add_history('Inserted missing RDNOISE header card, assumed RDNOISE = 0.0')
  data.close()
Пример #6
0
def removefiles(*patterns):

  """
     Remove files with UNIX-like filename pattern matching.
     No questions asked, so be careful!
  """

  # 'patterns' is an array or tuple of filename patterns. Can, of course,
  # also be inidividual file names.

  # Loop over patterns
  for pattern in patterns:

    # Split off the directory path
    (dirname, filepattern) = os.path.split(pattern)
    
    # Get a list of all files in the directory
    try:
      filelist = os.listdir(dirname)
    except OSError, errstr:
      # Succeed even if the directory was not there (and put warning in log)
      messageLog.put_warning(errstr, 9)
      return

    # Check each file in the directory list
    for file in filelist:

      # And see if it's name matches the pattern
      if fnmatch.fnmatch(file, filepattern):

        # If yes, (try to) remove it from the system
	messageLog.put('Removing file : "%s"' % file, 7)
        try:
          os.remove(os.path.join(dirname, file))
	except OSError, errstr:
          # Succeed even if there were no files (and put warning in log)
	  messageLog.put_warning(errstr, 9)
Пример #7
0
def removefiles(*patterns):
    """
     Remove files with UNIX-like filename pattern matching.
     No questions asked, so be careful!
  """

    # 'patterns' is an array or tuple of filename patterns. Can, of course,
    # also be inidividual file names.

    # Loop over patterns
    for pattern in patterns:

        # Split off the directory path
        (dirname, filepattern) = os.path.split(pattern)

        # Get a list of all files in the directory
        try:
            filelist = os.listdir(dirname)
        except OSError, errstr:
            # Succeed even if the directory was not there (and put warning in log)
            messageLog.put_warning(errstr, 9)
            return

        # Check each file in the directory list
        for file in filelist:

            # And see if it's name matches the pattern
            if fnmatch.fnmatch(file, filepattern):

                # If yes, (try to) remove it from the system
                messageLog.put('Removing file : "%s"' % file, 7)
                try:
                    os.remove(os.path.join(dirname, file))
                except OSError, errstr:
                    # Succeed even if there were no files (and put warning in log)
                    messageLog.put_warning(errstr, 9)
Пример #8
0
def save(outfile=None, optionlist=[], ignore_options=[]):

  "Save the current values of options to disk"

  # Only proceed if outfile is defined
  if outfile:

    # Create an object that will only contain the elements that are
    # supposed to be written to disk
    outconfig = configobj.ConfigObj(options['default_savefile'].value)

    # If optionlist is defined, only select the listed items
    if optionlist: keys = optionlist
    # Otherwise, select all
    else: keys = outconfig['main'].keys()

    # If ignore_options was defined, modify the keylist
    if ignore_options: 
      for ignore_key in ignore_options:
        if ignore_key in keys:
	  keys.remove(ignore_key)

    # Loop over options
    for key in keys:

      try:
        outvalue = options[key].value
      except KeyError:
	messageLog.put_warning("Could not write value for %s - continuing" % key)
        return

      # Process arrays of files
      if options[key].type in ('filelist', 'fitsfilelist'):

        # Cast to array of strings
        if type(outvalue) != type([]): outvalue = [outvalue]

        # And attach directory name is necessary
	filelist = outvalue
	outvalue = []

	for filename in filelist:
	  if os.path.dirname(filename) == options[key].indir.value :
	    filename = os.path.basename(filename)
          outvalue.append(filename)

      if options[key].type in ('fitsfile', 'outfitsfile'):
	if os.path.dirname(outvalue) == options[key].indir.value :
	  outvalue = os.path.basename(outvalue)


      # Copy the options into the output object
      try:
        outconfig['main'][key] = outvalue
	messageLog.put("Wrote value for %s to %s" % (key, outfile), 9)
      except KeyError:
        messageLog.put_error("Cannot write configuration option '%s'." % key)

    try:
      # Now, open the output file in write mode
      filehandle = open(outfile, mode='w')
      # Write the data
      outconfig.write(filehandle)
      # Close the output file
      filehandle.close()
    except Exception, errstr:
      messageLog.put_error("Could not save config to %s : %s" % (outfile, errstr))

    # Inform user
    messageLog.put("Wrote configuration to %s" % outfile, 5)
Пример #9
0
    def findNewFiles(self):
        """
       Find files that are not yet listed in the queue of unprocessed
       files, and match these with the filename filter pattern
    """

        # Retrieve the current value of the filename pattern from the widget
        pattern = config.options['filename_filter'].value

        # Retrieve the current value of the data input directory
        inpath = config.options['inpath'].value

        # Read the directory contents
        contents = [os.path.join(inpath, file) for file in os.listdir(inpath)]

        # Check the obtained list of files agains the existing directory list
        # Remove files that already existed in the directory list

        # ORDER of if-statements is important!

        iterlist = copy.copy(self.dirlist)
        for file in iterlist:

            if file not in contents:
                # Hmm... a strange situation. Apparently a file listed in self.dirlist
                # DISappeared from the directory. Adjust the lists accordingly
                messageLog.put_warning(
                    'File %s disappeared from directory - updating lists' %
                    file, 5)
                self.dirlist.remove(file)

                # Do NOT swap the following two statements!

                # Is this file already in the list of processed files?
                if file in self.reducedfiles: self.reducedfiles.remove(file)

                # Is this file already in the list of unprocessed files?
                if file in self.newfiles: self.newfiles.remove(file)

            if file in contents:
                # Normal situation, all files in self.dirlist are also in the current
                # directory listing. Remove these files one-by-one from the list, so that
                # the remaining files are those that are the new files in the input
                # directory (this time).
                contents.remove(file)

        # Now loop over the remaining files
        for file in contents:

            # And append these to 'self.dirlist' for future reference
            self.dirlist.append(file)

            # Only look at the filename (disregard from directory path)
            basename = os.path.basename(file)

            # Make sure that (1) the 'file' is not a directory entry, (2) that it
            # matches the filename filter pattern, and (3) that it not yet in
            # the unprocessed or processed list.
            if ((not os.path.isdir(file))
                    and (fnmatch.fnmatch(basename, pattern))
                    and (file not in self.reducedfiles)
                    and (file not in self.newfiles)):

                messageLog.put('Appending file to the queue', 9)

                # Only then, append the file to the list of files to process.
                self.newfiles.append(file)

        # Finally, update queue sizes on screen
        self.updateQueueSize()
Пример #10
0
def flipframe(frame, direction=0, extension=0):

  # Retrieved the x and y coordinate vectors
  (xvec, yvec) = WCSvector(frame, extension)


  # MMM... MAYBE NOT TRUE... 
  # Comment : flipping the xvec and yvec below is not really
  # necessary, because the xnewmin and ynewmin will still be the
  # lowest pixel number in the (flipped) xvec and yvec. However,
  # exchanging the xvec and yvec is _very_ relevant.

  # NB: xaxis is axis nr 1, and yaxis is axis nr 0

  flippeddata = frame[extension].data

  # Rotate 'n' times 90 degrees
  # x ->  x, y ->  y
  if   direction == 0:
    flippeddata = flippeddata

  # x -> -y, y ->  x
  elif direction == 1:
    flippeddata = _flipaxis(flippeddata, axis=1)
    flippeddata.transpose()
    yvec = _flipaxis(yvec, axis=0)

  # x -> -x, y -> -y
  elif direction == 2:
    flippeddata = _flipaxis(flippeddata, axis=1)
    flippeddata = _flipaxis(flippeddata, axis=0)
    xvec = _flipaxis(xvec, axis=0)
    yvec = _flipaxis(yvec, axis=0)

  # x ->  y, y -> -x
  elif direction == 3:
    flippeddata = _flipaxis(flippeddata, axis=0)
    flippeddata.transpose()
    xvecnew = yvec
    yvec = _flipaxis(xvec, axis=0)
    xvec = xvecnew
    del(xvecnew)

  # Rotate 'n' times 90 degrees, and transpose
  # x ->  y, y ->  x
  elif direction == 4:
    flippeddata.transpose()
    xvecnew = yvec
    yvec = xvec
    xvec = xvecnew
    del(xvecnew)

  # x -> -x, y ->  y
  elif direction == 5:
    flippeddata = _flipaxis(flippeddata, axis=1)
    xvec = _flipaxis(xvec, axis=0)

  # x -> -y, y -> -x
  elif direction == 6:
    flippeddata = _flipaxis(flippeddata, axis=1)
    flippeddata = _flipaxis(flippeddata, axis=0)
    flippeddata.transpose()

  # x ->  x, y -> -y
  elif direction == 7:
    flippeddata = _flipaxis(flippeddata, axis=0)
    yvec = _flipaxis(yvec, axis=0)

  else:
    messageLog.put_warning('Frame orientation parameter out of range - ignored')


  # Determine 'new' vector minima and stepsize
  xnewbin = abs(xvec[2] - xvec[1])
  ynewbin = abs(yvec[2] - yvec[1])

  xnewmin = xvec.min()
  ynewmin = yvec.min()


  # Update header values accordingly
  frame[extension].header.update('CRVAL1', xnewmin)
  frame[extension].header.update('CRPIX1', 1)
  frame[extension].header.update('CDELT1', xnewbin)
  frame[extension].header.update('CTYPE1', 'PIXEL')

  frame[extension].header.update('CRVAL2', ynewmin)
  frame[extension].header.update('CRPIX2', 1)
  frame[extension].header.update('CDELT2', ynewbin)
  frame[extension].header.update('CTPYE2', 'PIXEL')

  # Store the rotated and flipped data in fits object
  frame[extension].data = flippeddata

  # And adjust the image headers to reflect the data
  frame[extension].update_header()
Пример #11
0
def run():
    """
     Main program entry, initializing and starting the main window and managing
     its 'mainloop'
  """

    # Get and check command-line options
    #
    # NB: The -c (enable-calib) option is now deprecated
    #
    try:
        opts, args = getopt.getopt(sys.argv[1:], "cs", ["calib", "scrollbar"])
    except getopt.GetoptError:
        # print help information and exit:
        print "Unknown command line parameter. Allowed optional parameters are : "
        #print "-c [file]            Load calibration file at startup"
        print "-s / --scrollbar	Add vertical scrollbar is screen does not fit"
        sys.exit(2)

    #enablecalib  = False
    hasscrollbar = False

    for option, parameter in opts:
        if option in ("-c", "--calib"):
            print "The -c option is deprecated. You can now always access the calibs submenu."
        #  enablecalib = True
        if option in ("-s", "--scrollbar"):
            hasscrollbar = True

    # Display a welcome message in the message log
    messageLog.put('=' * 65)
    messageLog.put("Welcome to FIEStool - the automatic data processing tool")
    messageLog.put("for the FIES spectrograph at the Nordic Optical Telescope")
    messageLog.put("Copyright (c) 2005 NOTSA; All Rights Reserved")
    messageLog.put("This is version %s (%s), written by %s" %
                   (_version, _date, _author))
    messageLog.put('=' * 65)
    messageLog.put("Right-click on any item to obtain instantaneous help")
    messageLog.put('=' * 65)

    # And tell the user what warnings and errors look like
    messageLog.put_warning("This is a sample warning")
    messageLog.put_error("This is a sample error")
    messageLog.put('=' * 65)

    # Now, check if a default config file exists; if yes, load it.
    try:
        config.load(config.options['default_savefile'].value)
    except IOError:
        messageLog.put_warning('No default configuration file found!')
        pass

    # Now, try to load the autoLoader config file.
    try:
        config.load_raw(config.options['default_autoload_savefile'].value)
    except IOError:
        messageLog.put_warning(
            'No default autoloader configuration file found!')
        pass

    # Create the absolute top-level root window. Destroying this kills 'everything'!
    root = Tk()

    # Make the root window variable in width and height
    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    # Give it a title
    root.title('FIEStool - Data reduction for the FIES spectrograph')

    # Initialize the main window, with 'root' as its parent
    main = mainWindow(root, hasscrollbar)

    if hasscrollbar:

        # This puts the focus right, and will give the canvas child real sizes
        root.deiconify()
        root.focus_set()
        root.grab_set()
        root.wait_visibility()

        # Show the canvas
        main.canvas.grid(row=0, column=0, sticky=N + S + E + W)

        # Determine width and heigth of the contained frame
        canvaschild = main.canvas.children.values()[0]

        neededwidth = canvaschild.winfo_width()
        neededheight = canvaschild.winfo_height()

        maxheight = main.canvas.winfo_screenheight()
        maxwidth = main.canvas.winfo_screenwidth()

        neededheight = min(maxheight, neededheight)
        neededwidth = min(maxwidth, neededwidth)

        # Set the canvas size and enable scrolling
        main.canvas.config(width=neededwidth, height=neededheight)
        main.canvas.config(scrollregion=main.canvas.bbox("all"))

    else:

        # Display the main window
        main.grid(sticky=N + E + S + W)

    # Catch the 'delete window' signal that may be triggered by the window
    # manager if the user clicks on its 'close' button
    root.protocol("WM_DELETE_WINDOW", main.really_quit)

    # Start the mainloop. The program executing pauses here, only to execute events
    # generated by the main window (which in essence is everything)

    ###############
    main.mainloop()
    ###############

    # Apparently an event triggered an exit out of the mainloop (see main.reallyquit)
    messageLog.put("Destroying widgets")

    # Destroy the root window and all its children
    root.destroy()

    # This message will never arrive, though...
    messageLog.put("Program finished - EXIT FIEStool")

    # Return to caller (main)
    return
Пример #12
0
def run():

  """
     Main program entry, initializing and starting the main window and managing
     its 'mainloop'
  """

  # Get and check command-line options
  #
  # NB: The -c (enable-calib) option is now deprecated
  #
  try:
    opts, args = getopt.getopt(sys.argv[1:], "cs", ["calib", "scrollbar"])
  except getopt.GetoptError:
    # print help information and exit:
    print "Unknown command line parameter. Allowed optional parameters are : "
    #print "-c [file]            Load calibration file at startup"
    print "-s / --scrollbar	Add vertical scrollbar is screen does not fit"
    sys.exit(2)

  #enablecalib  = False
  hasscrollbar = False

  for option, parameter in opts:
    if option in ("-c", "--calib"):
      print "The -c option is deprecated. You can now always access the calibs submenu."
    #  enablecalib = True
    if option in ("-s", "--scrollbar"):
      hasscrollbar = True

  # Display a welcome message in the message log
  messageLog.put('='*65)
  messageLog.put("Welcome to FIEStool - the automatic data processing tool")
  messageLog.put("for the FIES spectrograph at the Nordic Optical Telescope")
  messageLog.put("Copyright (c) 2005 NOTSA; All Rights Reserved")
  messageLog.put("This is version %s (%s), written by %s" % (_version, _date, _author))
  messageLog.put('='*65)
  messageLog.put("Right-click on any item to obtain instantaneous help")
  messageLog.put('='*65)

  # And tell the user what warnings and errors look like
  messageLog.put_warning("This is a sample warning")
  messageLog.put_error("This is a sample error")
  messageLog.put('='*65)

  # Now, check if a default config file exists; if yes, load it.
  try:
    config.load(config.options['default_savefile'].value)
  except IOError:
    messageLog.put_warning('No default configuration file found!')
    pass

  # Now, try to load the autoLoader config file.
  try:
    config.load_raw(config.options['default_autoload_savefile'].value)
  except IOError:
    messageLog.put_warning('No default autoloader configuration file found!')
    pass

  # Create the absolute top-level root window. Destroying this kills 'everything'!
  root = Tk()

  # Make the root window variable in width and height
  root.rowconfigure(0, weight=1)
  root.columnconfigure(0, weight=1)

  # Give it a title
  root.title('FIEStool - Data reduction for the FIES spectrograph')

  # Initialize the main window, with 'root' as its parent
  main = mainWindow(root, hasscrollbar)

  if hasscrollbar:

    # This puts the focus right, and will give the canvas child real sizes
    root.deiconify()
    root.focus_set()
    root.grab_set()
    root.wait_visibility()

    # Show the canvas
    main.canvas.grid(row=0, column=0, sticky=N+S+E+W)

    # Determine width and heigth of the contained frame
    canvaschild  = main.canvas.children.values()[0]

    neededwidth  = canvaschild.winfo_width()
    neededheight = canvaschild.winfo_height()

    maxheight = main.canvas.winfo_screenheight()
    maxwidth  = main.canvas.winfo_screenwidth()

    neededheight = min(maxheight, neededheight)
    neededwidth  = min(maxwidth,  neededwidth)

    # Set the canvas size and enable scrolling
    main.canvas.config(width=neededwidth, height=neededheight)
    main.canvas.config(scrollregion=main.canvas.bbox("all"))

  else:

    # Display the main window
    main.grid(sticky=N+E+S+W)

  # Catch the 'delete window' signal that may be triggered by the window
  # manager if the user clicks on its 'close' button
  root.protocol("WM_DELETE_WINDOW", main.really_quit)


  # Start the mainloop. The program executing pauses here, only to execute events
  # generated by the main window (which in essence is everything)

  ###############
  main.mainloop()
  ###############


  # Apparently an event triggered an exit out of the mainloop (see main.reallyquit)
  messageLog.put("Destroying widgets")

  # Destroy the root window and all its children
  root.destroy()


  # This message will never arrive, though...
  messageLog.put("Program finished - EXIT FIEStool")

  # Return to caller (main)
  return
Пример #13
0
def getwavedef2D(frame):

  """
     Extract and return the wavelength definition vector [ordernumber, zero
     wavelength offset, wavelength step] using the FITS header keywords of a
     2-dimensional frame, following the IRAF definition. It is a rather dirty
     routine.
  """

  # Make sure I can find the appropriate headers
  if not (frame[0].header.get('WAT0_001', None)):
    messageLog.put_warning('Could not find FITS headers defining the wavelength solution')
    return None

  # Get the number of pixels per order and the number of orders from the headers
  npixels = frame[0].header['NAXIS1']
  norders = frame[0].header['NAXIS2']

  # Create an (empty) output array
  wavearray   = numpy.zeros((norders, 4), dtype=numpy.float64)

  # Get all the FITS header keywords in an array
  fullhlist =  frame[0].header.items()
  wavedef = ""

  # And loop over the headers to extract any header that contains (part of)
  # the wavelength definition, and store these in one long string object
  for name, value in fullhlist:
    # Only 68 characters can be part of FITS header value
    if name[0:4] == 'WAT2': wavedef = wavedef + value.ljust(68)

  # Now it gets very IRAF-specific! Bin the first 16 characters (garbage)
  wavedef = wavedef[16:]
  # and split the string object into smaller pieces, one for each order
  wavedef = wavedef.split('spec')

  # Bin the first entry (also garbage)  
  del(wavedef[0])

  # Check that we got the correct number of order definitions from the headers
  if len(wavedef) != norders:
    messageLog.put_warning('No wavelength solution found', 5)
    return None

  # Initialize order number counter
  linecounter = 0

  # Get rid of " and = characters in the string and isolate the parameter values
  for line in wavedef:
    line = line.replace('"', '')
    line = line.replace('=', '')
    line = line.split()

    # According to IRAF's definition the following parameters can be extracted
    # from the line

    number  = int(line[1])
    orderno = int(line[2])
    woffset = float(line[4])
    wstep   = float(line[5])

    # Store the order number, zero pixel wavelength and wavelength step in the
    # output array
    wavearray[linecounter, :]   = [number, orderno, woffset, wstep]
    linecounter = linecounter + 1

  # If an incompatible format was used to define the order locations, complain!
  if int(line[3]) != 0:
    messageLog.put_error('Error in interpreting spectrum definition')
    messageLog.put_warning('Dispersion solution format seems to be non-linear. Trying to make ')
    messageLog.put_warning('the best of it. Wavelength scale may be terribly wrong.')

  # Finally, return the result to the caller
  return wavearray