"""
Manipulating the ULT fridge Platinum Thermometer logs.

Changelog (partial)
25 Jun 2009, Lev
handle empty logs in the "PLMLog.interpolate" method

29 Sep 2009, Lev
add 'trace_filename' method

12 Jul 2010, Lev
add 'set_T' method
"""

import os
import os.path
import time
import datetime
import numpy
import matplotlib.dates
import flib
import timelog
import ascii2numpy

class PLMLog(timelog.TimeLog):
    """
    Represents a ULT PLM4 thermometry log over a certain time.
    Two logs can be concatenated with a '&' sign

    log format:
    1  [:,0]  Time (sec, since 00:00 1 January 1970 GMT)
    2  [:,1]  Curie Temperature [mK] T = B / (M0 - A)
    3  [:,2]  Magnetisation M0
    4  [:,3]  Transmitter Pulse Length
    5  [:,4]  NS MCT Temperature (mK, Greywall scale)
    6  [:,5]  NS MCT Bridge Ratio
    7  [:,6]  Background (stabilised)
    8  [:,7]  Gain Stability Index
    9  [:,8]  Gain
    10 [:,9]  Integration delay
    11 [:,10] Integration time
    12 [:,11] 0 if PLM was overloaded, otherwise 1
    """

    @staticmethod
    def fieldcount(): "return number of columns in the self.data"; return 12
    
    def T(self): "temperature"; return self.data[:,1]
    def M0(self): "M0"; return self.data[:,2]
    def xmit(self): "transmitter pulse length"; return self.data[:,3].astype('i')
    def T_MCTNS(self): "MCTNS Temp"; return self.data[:,4]
    def r_MCTNS(self): "MCTNS ratio"; return self.data[:,5]
    def bg(self): "raw background"; return self.data[:,6]
    def stabi(self): "gain stability index"; return self.data[:,7]
    def gain(self): "PLM-4 gain"; return self.data[:,8].astype('i')
    def idelay(self): "integration delay"; return self.data[:,9]
    def itime(self): "integration time"; return self.data[:,10]
    def overload(self): "True if the measurement overloaded PLM-4, otherwise False"; return self.data[:,11] == 0
    def rawM0(self): "raw (unstabilised) M0"; return self.M0()*self.stabi() + self.bg()
    
    def set_T(self, T): self.data[:,2] = T

    def at(self, xmit=None, gain=None, idelay=None, itime=None, overload=None):
        """
        Return a subset of the log obtained for at given settings. Any of
        the paramameters, xmit, gain, idelay, itime, overload is used for
        filtering points if it is specified (is not 'None').
        """
        
        subset = self.M0() == self.M0()
        
        if xmit is not None:
            subset &= (self.xmit() == xmit)
        if gain is not None:
            subset &= (self.gain() == gain)
        if idelay is not None:
            subset &= (self.idelay() == idelay)
        if itime is not None:
            subset &= (self.itime() == itime)
        if overload is not None:
            subset &= (self.overload() == overload)
        
        return self.subset(subset)

    def atxmit(self, xmit): return self.at(xmit=xmit)

    def xmits(self):
        """
        Return a list of transmitter pulse lengths used present in the log
        """
        return sorted(set(self.xmit()))

    def __repr__(self): return "PLMLog %s, xmit=%s" % (self.datespan(), self.xmits())

    def interpolate(self, t, heatleak = 0.07):
        """
        Return interpolated PLM temperature for time(s) in 't'.
        'heatlink' [mK^2] is used to determine nuclear stage temperature
        Tns = (Tsp^2 - heatleak)^0.5
        Then 1/Tns vs time is interpolated linearly and PLM temperature
        is calculated back. 0.07 mK^2 is the value about right for ULT RUN 20
        """
        if len(self) < 1:
            return numpy.nan * t
        Tns = (self.T()**2 - heatleak)**0.5
        Tns = 1./numpy.interp(t, self.t(), 1./Tns, left=numpy.nan, right=numpy.nan)
        Tpt = (Tns**2 + heatleak)**0.5
        return Tpt

    @staticmethod
    def load(filename):
        """
        Load PLM log from a given file
        The function determines file format from the name:
            3. '????????plm-4.dat' run 20+, since 24 March 2008
            2. '????????plm4.dat'  early on run 20 (before 24 March 2008)
            1. '????????_plm4.dat' prior to run 20
        
        If a file read is in format 1 or 2, the following assumptions are made
        (the corresponding data were not recorded):
            
            variable gain defaults to 10
            integration delay to 55
            integration time to 125
            PLM is considered not to overload if M0 > 0
        """
        
        basename = os.path.basename(filename)
        if basename[8:] == 'plm-4.dat':
            # Loads a new (Run 20+, since 24 March 2008) format PLM log from a given file
            #
            # 1 [:,0] Time (sec, since 00:00 1 January 1970 GMT)
            # 2 [:,1] Curie Temperature [mK] T = B / (M0 - A)
            # 3 [:,2] Magnetisation M0
            # 4 [:,3] Transmitter Pulse Length
            # 5 [:,4] NS MCT Temperature (mK, Greywall scale)
            # 6 [:,5] NS MCT Bridge Ratio
            # 7 [:,6] Background (stabilised)
            # 8  [:,7]  Gain Stability Index
            # 9  [:,8]  Gain (or Gain Code - 0 for 10, 1 for 20, 2 for 50, 3 for 100)
            # 10 [:,9]  Integration delay
            # 11 [:,10] Integration time
            # 12 [:,11] 0 if PLM was overloaded, otherwise 1
            
            data = ascii2numpy.loadascii(filename, cols=12)
#            data = flib.loadascii(filename, usecols=range(12))
            
            # timestamps are stored as seconds since 00:00 1 Jan 1904.
            # Convert to seconds since 00:00 1 Jan 1970, C and UNIX time format.
            data[:,0] = flib.labview2tm(data[:,0])
            
            # for a short time gain code was stored instead of gain, 
            # e.g. 0 for 10, 1 for 20, 2 for 50, 3 for 100
            # correct this:
            data[data[:,8] == 0,8] = 10
            data[data[:,8] == 1,8] = 20
            data[data[:,8] == 2,8] = 50
            data[data[:,8] == 3,8] = 100
            
            return PLMLog(data)

        elif basename[8:] == 'plm4.dat':
            # Loads PLM log in format used early on Run 20 (before 24 March 2008) from a given file
            # 
            # 1 [:,0] Time (sec, since 00:00 1 January 1970 GMT)
            # 2 [:,1] Curie Temperature [mK] T = B / (M0 - A)
            # 3 [:,2] Magnetisation M0
            # 4 [:,3] Transmitter Pulse Length
            # 5 [:,4] NS MCT Temperature (mK, Greywall scale)
            # 6 [:,5] NS MCT Bridge Ratio
            # 7 [:,6] Background (stabilised)
            # 8 [:,7] Gain (stabilised) - proper name is gain stability index
            #
            # skip 10 rows because of the "Created on ..." is not preceded by a '#'.
#            rawdata = flib.loadascii(filename, skiprows=10, usecols=range(8))
            rawdata = ascii2numpy.loadascii(filename, cols=8)
            
            # fill with NaN's
            data = 0./ numpy.zeros((len(rawdata), 12))
            data[:,0:8] = rawdata[:,0:8]
            
            # fill the absent columns, hope these are the correct values
            # 9  [:,8]  Gain
            # 10 [:,9]  Integration delay
            # 11 [:,10] Integration time
            # 12 [:,11] 0 if PLM was overloaded, otherwise 1
            data[:,8] = 10
            data[:,9] = 55
            data[:,10] = 125
            data[:,11] = (data[:,2] > 0) # assume no overload if M0 > 0
            
            # timestamps are stored as seconds since 00:00 1 Jan 1904.
            # Convert to seconds since 00:00 1 Jan 1970, C and UNIX time format.
            data[:,0] = flib.labview2tm(data[:,0])
            return PLMLog(data)
        elif basename[8:] == '_plm4.dat':
            # Loads an old (prior Run 20) format PLM log from a given file.
            # Filename is used to determine the date stamp.
            #
            # file format is:
            # 1 [:,0] - time, seconds since start of the day
            # 2 [:,1] - background
            # 3 [:,2] - gain stab. index
            # 4 [:,3] - M0
            # 5 [:,4] - Temperature
            # ... the rest is omitted. Contains some string and time since 00:00 1 Jan 1904.
            rawdata = ascii2numpy.loadascii(filename, cols=6)
            #rawdata = flib.loadascii(filename, usecols=(0,1,2,3,4,5))
            
            # fill with NaN's
            data = 0./ numpy.zeros((len(rawdata), 12))
            
            # date is encoded in the filename
            year = int(basename[0:4])
            month = int(basename[4:6])
            day = int(basename[6:8])
            
            data[:,0] = rawdata[:,0] + time.mktime(datetime.date(year, month, day).timetuple())
            data[:,1] = rawdata[:,4]
            data[:,2] = rawdata[:,3]
            data[:,3] = rawdata[:,5]
            data[:,6] = rawdata[:,1]
            data[:,7] = rawdata[:,2]
            
            # fill the absent columns, hope these are the correct values
            # 9  [:,8]  Gain
            # 10 [:,9]  Integration delay
            # 11 [:,10] Integration time
            # 12 [:,11] 0 if PLM was overloaded, otherwise 1
            data[:,8] = 10
            data[:,9] = 55
            data[:,10] = 125
            data[:,11] = (data[:,2] > 0) # assume no overload if M0 > 0
            
            return PLMLog(data)
        else:
            # other file formats are not recognised
            return None
    
    @staticmethod
    def loadrun(run, start = None, end = None):
        """
        Return plm log for a given run, withing [start, end] period if boundaries are specified.
        """
        logdir = flib.smbpath('//phpc338/Run20/PLM4' if run == 20 else 'C:/pub/plm/run%d'%run)
        return PLMLog.loaddir(logdir, start=start, end=end).period(start, end)

    def trace_filename(self, i):
        """
        Return a standard filename 'YYYYMMDD/xplm-XMIT-GAIN-TIME.dat'
        containing the FID of the 'i'-th entry of the log
        """
        return os.path.join(flib.dt2filename(self.t()[i]), \
                'xplm-%d-%d-%.2f.dat' % (self.xmit()[i], self.gain()[i], flib.tm2labview(self.t()[i])))
