Пример #1
0
class TestParamReader(unittest.TestCase):
    """Test the Utility functions."""

    def setUp(self):

        self.configDir = os.path.join(getModulePath(), "tests", "testData")
        self.fileName = "zkAndDofIdxArraySet.txt"

        filePath = os.path.join(self.configDir, self.fileName)
        self.paramReader = ParamReader(filePath=filePath)

    def testGetSetting(self):

        arrayParamList = ["icomp", "izn3"]
        znmax = self.paramReader.getSetting("znmax",
                                            arrayParamList=arrayParamList)
        self.assertEqual(znmax, "22")

        icomp = self.paramReader.getSetting("icomp",
                                            arrayParamList=arrayParamList)
        icomp = np.array(icomp.split(), dtype=int)
        self.assertEqual(np.sum(icomp), 10)

        izn3 = self.paramReader.getSetting("izn3",
                                           arrayParamList=arrayParamList)
        izn3 = np.array(izn3.split(), dtype=int)
        self.assertEqual(np.sum(izn3), 12)

        self.assertRaises(ValueError, self.paramReader.getSetting,
                          "notThisSetting")

    def testGetContent(self):

        content = self.paramReader.getTxtContent()
        contentList = content.splitlines()
        self.assertEqual(len(contentList), 24)

    def testFilePath(self):

        ansFilePath = os.path.join(self.configDir, self.fileName)
        self.assertEqual(self.paramReader.getFilePath(), ansFilePath)

    def testConstructor(self):

        paramReader = ParamReader()
        self.assertEqual(paramReader.getFilePath(), "")
        self.assertEqual(paramReader.getTxtContent(), "")

        matContent = paramReader.getMatContent()
        self.assertEqual(len(matContent), 0)
        self.assertTrue(isinstance(matContent, np.ndarray))
Пример #2
0
class DataShare(object):
    def __init__(self):
        """Initialization of data share class."""

        self.configDir = ""
        self.instName = InstName.LSST

        self.zn3Idx = np.array([], dtype=int)
        self.dofIdx = np.array([], dtype=int)

        self._zkAndDofIdxArraySetFile = ParamReader()
        self._senMfile = ParamReader()
        self._mappingFile = ParamReader()
        self._idxDofFile = ParamReader()
        self._sensorIdToNameFile = ParamReader()

    def config(self,
               configDir,
               instName=InstName.LSST,
               zkAndDofIdxArraySetFileName="zkAndDofIdxArraySet.txt",
               mappingFileName="sensorNameToFieldIdx.txt",
               idxDofFileName="idxDOF.txt",
               sensorIdToNameFileName="sensorIdToName.txt"):
        """Do the configuration of DataShare class.

        zk: Annular Zernike polynomial.
        DOF: Degree of Freedom.

        Parameters
        ----------
        configDir : str
            Configuration directory.
        instName : enum 'InstName', optional
            Instrument name. (the default is InstName.LSST.)
        zkAndDofIdxArraySetFileName : str, optional
            File name of zk and DOF index array set. (the default is
            "zkAndDofIdxArraySet.txt".)
        mappingFileName : str, optional
            File name of mapping abbreviated sensor name to index of optical
            field.  (the default is "sensorNameToFieldIdx.txt".)
        idxDofFileName : str, optional
            Index of DOF file name. (the default is "idxDOF.txt".)
        sensorIdToNameFileName : str, optional
            Configuration file name to map sensor Id to name. (the default
            is "sensorIdToName.txt".)
        """

        self.configDir = configDir
        self.instName = instName

        zkAndDofIdxArraySetFilePath = os.path.join(
            configDir, zkAndDofIdxArraySetFileName)
        self._zkAndDofIdxArraySetFile = ParamReader(
            filePath=zkAndDofIdxArraySetFilePath)

        mappingFilePath = os.path.join(self.getInstDir(), mappingFileName)
        self._mappingFile = ParamReader(filePath=mappingFilePath)

        idxDofFilePath = os.path.join(configDir, idxDofFileName)
        self._idxDofFile = ParamReader(filePath=idxDofFilePath)

        sensorIdToNameFilePath = os.path.join(configDir,
                                              sensorIdToNameFileName)
        self._sensorIdToNameFile = ParamReader(filePath=sensorIdToNameFilePath)

        senMfilePath = self._getSenMfilePath(reMatchStr=r"\AsenM\S+")
        self._senMfile = ParamReader(filePath=senMfilePath)

        self._readZn3AndDofIdxArray()

    def _readZn3AndDofIdxArray(self):
        """Read the Z3-Zn and DOF index arrays.

        DOF: degree of freedom.
        """

        arrayParamList = ["izn3", "icomp"]

        self.zn3Idx = self._zkAndDofIdxArraySetFile.getSetting(
            "izn3", arrayParamList)
        self.zn3Idx = self._getNonZeroIdxFronStrArray(self.zn3Idx)

        self.dofIdx = self._zkAndDofIdxArraySetFile.getSetting(
            "icomp", arrayParamList)
        self.dofIdx = self._getNonZeroIdxFronStrArray(self.dofIdx)

    def _getNonZeroIdxFronStrArray(self, strArray):
        """Get the non-zero index array from the string array.

        Parameters
        ----------
        strArray : str
            String array.

        Returns
        -------
        numpy.ndarray
            Non-zero index array.
        """

        array = np.array(strArray.split(), dtype=int)

        return self._getNonZeroIdx(array)

    def _getNonZeroIdx(self, array):
        """Get the non-zero index array.

        Parameters
        ----------
        array : numpy.ndarray or list
            Array to look for the non-zero index.

        Returns
        -------
        numpy.ndarray
            Non-zero index array.
        """

        return np.where(np.array(array) != 0)[0]

    def _getSenMfilePath(self, reMatchStr):
        """Get the sensitivity matrix M file path.

        Parameters
        ----------
        reMatchStr : str
            Matching string for the regular expression.

        Returns
        -------
        str
            Sensitivity matrix M file path.
        """

        filePaths = getDirFiles(self.getInstDir())
        senMFilePath = getMatchFilePath(reMatchStr, filePaths)

        return senMFilePath

    def getInstDir(self):
        """Get the instrument directory.

        Returns
        -------
        str
            Instrument directory.
        """

        return os.path.join(self.configDir, self.instName.name.lower())

    def getConfigDir(self):
        """Get the configuration directory.

        Returns
        -------
        str
            configuration directory.
        """

        return self.configDir

    def getSenM(self):
        """Get the sensitivity matrix M.

        The arrangement of M is (field #, zn #, dof #).

        Returns
        -------
        numpy.ndarray
            Sensitivity matrix M.
        """

        # Get the shape of sensitivity matrix M
        filePath = self._senMfile.getFilePath()
        fileName = os.path.basename(filePath)
        shape = self._getSenMshape(fileName)

        # Set the sensitivity matrix M
        senM = self._senMfile.getMatContent()
        senM = senM.reshape(shape)
        senM = senM[np.ix_(np.arange(shape[0]), self.zn3Idx, self.dofIdx)]

        return senM

    def _getSenMshape(self, senMFileName):
        """Get the shape of sensitivity matrix M.

        Parameters
        ----------
        senMFileName : str
            Sensitivity matrix M file name.

        Returns
        -------
        tuple
            Shape of sensitivity matrix M.

        Raises
        ------
        ValueError
            Cannot match the shape of M.
        """

        shape = None

        m = re.match(r"\S+_(\d+)_(\d+)_(\d+)\S+", senMFileName)
        if (m is not None):
            shape = m.groups()
            shape = tuple(map(int, shape))

        if shape is None:
            raise ValueError("Cannot match the shape of M.")

        return shape

    def getDofIdx(self):
        """Get the index array of degree of freedom (DOF).

        Returns
        -------
        numpy.ndarray
            Index array of DOF.
        """

        return self.dofIdx

    def getZn3Idx(self):
        """Get the index array of z3 to zn.

        Returns
        -------
        numpy.ndarray
            Index array of z3 to zn.
        """

        return self.zn3Idx

    def getFieldIdx(self, sensorNameList):
        """Get the list of field index based on the abbreviated sensor
        name (e.g. R22_S11) and mapping file.

        Parameters
        ----------
        sensorNameList : list
            List of abbreviated sensor name.

        Returns
        -------
        list
            Field index array.

        Raises
        ------
        TypeError
            The input type is not list.
        """

        fieldIdx = []
        if (self._inputIsList(sensorNameList)):
            for sensorName in sensorNameList:
                field = self._mappingFile.getSetting(sensorName)
                fieldIdx.append(int(field))

        return fieldIdx

    def _inputIsList(self, input):
        """Check the type of input is list or not.

        Parameters
        ----------
        input : obj
            Input.

        Returns
        -------
        bool
            True if the input's type is list.

        Raises
        ------
        TypeError
            The input type is not list.
        """

        inputIsList = True
        if (not isinstance(input, list)):
            inputIsList = False
            raise TypeError("The input type is '%s' instead of list." %
                            type(input))

        return inputIsList

    def getGroupIdxAndLeng(self, dofGroup):
        """Get the start index and length of specific group of degree of
        freedom (DOF).

        Parameters
        ----------
        dofGroup : enum 'DofGroup'
            DOF group.

        Returns
        -------
        int
            Start index of group.
        int
            Index length of group.

        Raises
        ------
        ValueError
            The input is not found in the idxDof file.
        """

        # Assign the parameter name
        if (dofGroup == DofGroup.M2HexPos):
            param = "M2_Hex_Pos"
        elif (dofGroup == DofGroup.CamHexPos):
            param = "Cam_Hex_Pos"
        elif (dofGroup == DofGroup.M1M3Bend):
            param = "M1M3_Bend"
        elif (dofGroup == DofGroup.M2Bend):
            param = "M2_Bend"
        else:
            raise ValueError("'%s' is not found in the '%s'." %
                             (dofGroup, self._idxDofFile.getFilePath()))

        # Get the values from the file
        startIdx, groupLeng = self._idxDofFile.getSetting(param)

        # Change the data type
        startIdx = int(startIdx)
        groupLeng = int(groupLeng)

        return startIdx, groupLeng

    def setZkAndDofIdxArrays(self, zn3Idx, dofIdx):
        """Set the index array of zn and degree of freedom (DOF).

        Parameters
        ----------
        zn3Idx : numpy.ndarray[int] or list[int]
            Index array of z3 to zn.
        dofIdx : numpy.ndarray[int] or list[int]
            Index array of DOF.
        """

        self.zn3Idx = np.array(zn3Idx, dtype=int)
        self.dofIdx = np.array(dofIdx, dtype=int)

    def setZkAndDofInGroups(self,
                            zkToUse=np.ones(19, dtype=int),
                            m2HexPos=np.ones(5, dtype=int),
                            camHexPos=np.ones(5, dtype=int),
                            m1m3Bend=np.ones(20, dtype=int),
                            m2Bend=np.ones(20, dtype=int)):
        """Set the index array of Zk and DOF in groups (M2 hexapod,
        camera hexapod, M1M3 bending mode, and M2 bending mode).

        For the element in input index array, use 1 for True and 0 for
        False. For example, if the m2HexPos is [1, 1, 1, 0, 0], only the
        first three positions will be used.

        Parameters
        ----------
        zkToUse : numpy.ndarray[int] or list[int], optional
            z3 to zn. (the default is np.ones(19, dtype=int))
        m2HexPos : numpy.ndarray[int] or list[int], optional
            M2 hexapod position. (the default is np.ones(5, dtype=int))
        camHexPos : numpy.ndarray[int] or list[int], optional
            Camera hexapod position. (the default is np.ones(5, dtype=int))
        m1m3Bend : numpy.ndarray[int] or list[int], optional
            M1M3 bending mode. (the default is np.ones(20, dtype=int))
        m2Bend : numpy.ndarray[int] or list[int], optional
            M2 bending mode. (the default is np.ones(20, dtype=int))

        Raises
        ------
        ValueError
            The length of 'zkToUse' is incorrect.
        ValueError
            "The length of DOF is incorrect."
        """

        zn3Max = self._getZn3Max()
        if (len(zkToUse) != zn3Max):
            raise ValueError("The length of 'zkToUse' should be %d." % zn3Max)

        # Get the index of zk to use
        zn3Idx = self._getNonZeroIdx(zkToUse)

        # Assign the order for the following iteration with zip
        dofInputs = [m2HexPos, camHexPos, m1m3Bend, m2Bend]

        dofIdx = np.array([], dtype=int)
        for dofInput, dofGroup in zip(dofInputs, DofGroup):
            startIdx, groupLeng = self.getGroupIdxAndLeng(dofGroup)

            if (len(dofInput) != groupLeng):
                raise ValueError("The length of DOF is incorrect.")

            idx = self._getNonZeroIdx(dofInput)
            dofIdx = np.append(dofIdx, idx + startIdx)

        self.setZkAndDofIdxArrays(zn3Idx, dofIdx)

    def _getZn3Max(self):
        """Get the number of terms of Z3 to Zn.

        Zn: Annular Zernike polynomial.

        Returns
        -------
        int
            Number of terms of Z3 to Zn.
        """

        zn3Max = self._zkAndDofIdxArraySetFile.getSetting("znmax")
        zn3Max = int(zn3Max) - 3

        return zn3Max

    def getWfAndFieldIdFromFile(self, wfFilePath, sensorNameList):
        """Get the wavefront error and field Id from the file.

        Parameters
        ----------
        wfFilePath : str
            Wavefront error file path.
        sensorNameList : list[str]
            List of abbreviated sensor name.

        Returns
        -------
        numpy.ndarray
            Wavefront error.
        list
            Field index list.

        Raises
        ------
        ValueError
            Number of sensors does not match the file.
        """

        wfErr = np.loadtxt(wfFilePath)
        fieldIdx = self.getFieldIdx(sensorNameList)

        if (len(fieldIdx) != wfErr.shape[0]):
            raise ValueError("Number of sensors does not match the file.")

        return wfErr, fieldIdx

    def getWfAndFieldIdFromShwfsFile(self, wfFilePath, sensorName="R22_S11"):
        """Get the wavefront error and field Id from the SHWFS file.

        SHWFS: Shack-Hartmann wavefront sensor.

        Parameters
        ----------
        wfFilePath : str
            Wavefront error file path.
        sensorName : str, optional
            Sensor name. (the default is "R22_S11", which uses the central
            position of main camera. This is to get the sensitivity matrix.)

        Returns
        -------
        numpy.ndarray
            Wavefront error.
        list
            Field index array.
        """

        # Only z3 to zn is considered
        wfErr = np.loadtxt(wfFilePath, usecols=1)[3:]
        fieldIdx = self.getFieldIdx([sensorName])

        return wfErr, fieldIdx

    def mapSensorIdToName(self, sensorIdList):
        """Map the list of sensor Id to sensor name.

        If no sensor name is found for a specific Id, there will be no returned
        value.

        Parameters
        ----------
        sensorIdList : list[int]
            List of sensor Id.

        Returns
        -------
        list
            List of abbreviated sensor names.
        int
            Number of sensors.
        """

        sensorNameList = []
        for sensorId in sensorIdList:
            try:
                sensorName = self._sensorIdToNameFile.getSetting(str(sensorId))
                sensorNameList.append(sensorName)
            except ValueError:
                pass

        return sensorNameList, len(sensorNameList)

    def mapSensorNameToId(self, sensorNameList):
        """Map the array of sensor name to sensor Id.

        Parameters
        ----------
        sensorNameList : list[str]
            List of abbreviated sensor names.

        Returns
        -------
        list[int]
            List of sensor Id.
        """

        sensorIdList = []
        if self._inputIsList(sensorNameList):

            content = self._sensorIdToNameFile.getTxtContent()

            for sensorName in sensorNameList:
                sensorIdList.append(
                    self._mapSensorNameToIdFromContent(content, sensorName))

        return sensorIdList

    def _mapSensorNameToIdFromContent(self, content, sensorName):
        """Map the sensor name to Id based on the mapping content.

        Parameters
        ----------
        content : str
            File content.
        sensorName : str
            Abbreviated sensor name.

        Returns
        -------
        int
            Sensor Id.

        Raises
        ------
        ValueError
            Can not find the sensor Id of input sensor name.
        """

        sensorId = None
        for line in content.splitlines():
            line = line.strip()

            # Skip the comment or empty line
            if line.startswith("#") or (len(line) == 0):
                continue

            sensorIdInFile, sensorNameInFile = line.split()

            if (sensorNameInFile == sensorName):
                sensorId = int(sensorIdInFile)
                break

        if (sensorId is None):
            raise ValueError("Can not find the sensor Id of '%s'." %
                             sensorName)

        return sensorId
Пример #3
0
class OptStateEstiDataDecorator(Decorator):
    def __init__(self, decoratedObj):
        """Initialization of optical state estimator data decorator class.

        Parameters
        ----------
        decoratedObj : obj
            Decorated object.
        """

        super(OptStateEstiDataDecorator, self).__init__(decoratedObj)

        self._intrincZkFileName = ""
        self._intrincZkFile = ParamReader()
        self._wavelengthTable = ParamReader()
        self._y2CorrectionFile = ParamReader()

    def configOptStateEstiData(self,
                               wavelengthTable="effWaveLength.txt",
                               intrincZkFileName="intrinsic_zn",
                               y2CorrectionFileName="y2.txt"):
        """Do the configuration of OptStateEstiDataDecorator class.

        Parameters
        ----------
        wavelengthTable : str, optional
            File name of effective wavelength for each filter in um. (the
            default is "effWaveLength.txt".)
        intrincZkFileName : str, optional
            Intric zk file name. (the default is "intrinsic_zn".)
        y2CorrectionFileName : str, optional
            y2 correction file name. (the default is "y2.txt".)
        """

        wavelengthTablePath = os.path.join(self.getConfigDir(),
                                           wavelengthTable)
        self._wavelengthTable = ParamReader(filePath=wavelengthTablePath)

        y2CorrectionFilePath = os.path.join(self.getInstDir(),
                                            y2CorrectionFileName)
        self._y2CorrectionFile = ParamReader(filePath=y2CorrectionFilePath)

        self._intrincZkFileName = intrincZkFileName

    def getEffWave(self, filterType):
        """Get the effective wavelength in um.

        This is based on the active filter type (U, G, R, I, Z, Y, REF). It
        is noted that "ref" means the monochromatic reference wavelength.

        Parameters
        ----------
        filterType : enum 'FilterType'
            Active filter type.

        Returns
        -------
        float
            Effective wavelength in um.
        """

        param = filterType.name.lower()
        effWave = float(self._wavelengthTable.getSetting(param))

        return effWave

    def getY2Corr(self, fieldIdx):
        """Get the y2 correction array. This is the zk offset between the
        camera center and corner.

        Parameters
        ----------
        fieldIdx : numpy.ndarray[int] or list[int]
            Field index array.

        Returns
        -------
        numpy.ndarray
            y2 correction array.
        """

        y2c = self._y2CorrectionFile.getMatContent()
        y2c = y2c[np.ix_(fieldIdx, self.getZn3Idx())]

        return y2c

    def getIntrinsicZk(self, filterType, fieldIdx):
        """Get the intrinsic zk of specific filter based on the array of
        field index.

        Parameters
        ----------
        filterType : enum 'FilterType'
            Active filter type.
        fieldIdx : numpy.ndarray[int] or list[int]
            Field index array.

        Returns
        -------
        numpy.ndarray
            Instrinsic zk of specific effective wavelength in um.
        """

        # Get the intrinsicZk file path
        reMatchStrTail = ""
        if (filterType != FilterType.REF):
            reMatchStrTail = "_" + filterType.name

        reMatchStr = r"\A%s%s[.]\S+" % (self._intrincZkFileName,
                                        reMatchStrTail)
        filePaths = getDirFiles(self.getInstDir())
        zkFilePath = getMatchFilePath(reMatchStr, filePaths)

        # Remap the zk index for z0-z2
        zkIdx = self.getZn3Idx() + 3

        # Get the intrinsicZk with the consideration of effective wavelength
        self._intrincZkFile = ParamReader(filePath=zkFilePath)
        intrinsicZk = self._intrincZkFile.getMatContent()
        intrinsicZk = intrinsicZk[np.ix_(fieldIdx, zkIdx)]
        intrinsicZk = intrinsicZk * self.getEffWave(filterType)

        return intrinsicZk