예제 #1
0
 def addGroup(self, name):
     gp = self.getGroup(name)
     if not gp:
         gp = HDFGroup()
         gp.id = name
         self.groups.append(gp)
     return gp
예제 #2
0
    def readHDF5(fp):
        root = HDFRoot()
        with h5py.File(fp, "r") as f:

            # set name to text after last '/'
            name = f.name[f.name.rfind("/")+1:]
            if len(name) == 0:
                name = "/"
            root.id = name

            # Read attributes
            #print("Attributes:", [k for k in f.attrs.keys()])
            for k in f.attrs.keys():
                root.attributes[k] = f.attrs[k].decode("utf-8")
                # Use the following when using h5toh4 converter:
                #root.attributes[k.replace("__GLOSDS", "")] = f.attrs[k].decode("utf-8")
            # Read groups
            for k in f.keys():
                item = f.get(k)
                #print(item)
                if isinstance(item, h5py.Group):
                    gp = HDFGroup()
                    root.groups.append(gp)
                    gp.read(item)
                elif isinstance(item, h5py.Dataset):
                    print("HDFRoot should not contain datasets")

        return root
예제 #3
0
    def processL1a(fp, calibrationMap):
        '''
        Reads a raw binary file and generates a L1a HDF5 file
        '''
        (_, fileName) = os.path.split(fp)

        # Generate root attributes
        root = HDFRoot()
        root.id = "/"
        root.attributes["HYPERINSPACE"] = MainConfig.settings["version"]
        root.attributes["CAL_FILE_NAMES"] = ','.join(calibrationMap.keys())
        root.attributes["WAVELENGTH_UNITS"] = "nm"
        root.attributes["LI_UNITS"] = "count"
        root.attributes["LT_UNITS"] = "count"
        root.attributes["ES_UNITS"] = "count"
        root.attributes["SATPYR_UNITS"] = "count"
        root.attributes["RAW_FILE_NAME"] = fileName
        root.attributes["PROCESSING_LEVEL"] = "1a"

        now = dt.datetime.now()
        timestr = now.strftime("%d-%b-%Y %H:%M:%S")
        root.attributes["FILE_CREATION_TIME"] = timestr
        # SZA Filter configuration parameter added to attributes below

        msg = f"ProcessL1a.processL1a: {timestr}"
        print(msg)
        Utilities.writeLogFile(msg)

        contextMap = collections.OrderedDict()

        for key in calibrationMap:
            cf = calibrationMap[key]
            gp = HDFGroup()
            gp.id = cf.instrumentType
            contextMap[cf.id] = gp

        # print("contextMap:", list(contextMap.keys()))
        # print("calibrationMap:", list(calibrationMap.keys()))
        print('Reading in raw binary data may take a moment.')
        RawFileReader.readRawFile(fp, calibrationMap, contextMap, root)

        # Populate HDF group attributes
        for key in calibrationMap:
            cf = calibrationMap[key]
            gp = contextMap[cf.id]
            # Don't add contexts that did not match any data in RawFileReader
            if 'CalFileName' not in gp.attributes:
                continue
            gp.attributes["InstrumentType"] = cf.instrumentType
            gp.attributes["Media"] = cf.media
            gp.attributes["MeasMode"] = cf.measMode
            gp.attributes["FrameType"] = cf.frameType
            gp.getTableHeader(cf.sensorType)
            gp.attributes[
                "DISTANCE_1"] = "Pressure " + cf.sensorType + " 1 1 0"
            gp.attributes["DISTANCE_2"] = "Surface " + cf.sensorType + " 1 1 0"
            # gp.attributes["SensorDataList"] = ", ".join([x for x in gp.datasets.keys()])
            gp.attributes["SensorDataList"] = ", ".join(
                list(gp.datasets.keys()))
            if gp.id != 'SAS' and gp.id != 'Reference':
                root.groups.append(gp)

        # Insure essential data groups are present before proceeding
        hld = 0
        hsl = 0
        hse = 0
        hed = 0
        gps = 0
        for gp in root.groups:
            if gp.id.startswith("HLD"):
                hld += 1
            if gp.id.startswith("HSL"):
                hsl += 1
            if gp.id.startswith("HSE"):
                hse += 1
            if gp.id.startswith("HED"):
                hed += 1
            if gp.id.startswith("GP"):
                gps += 1
        if hld != 2 or hsl != 2 or hse != 1 or hed != 1 or gps != 1:
            msg = "ProcessL1a.processL1a: Essential dataset missing. Check your configuration calibration files match cruise setup. Aborting."
            msg = f'{msg}\ngps: {gps} :1'
            msg = f'{msg}\nhed: {hed} :1'
            msg = f'{msg}\nhld: {hld} :2'
            msg = f'{msg}\nhse: {hse} :1'
            msg = f'{msg}\nhsl: {hsl} :2'
            print(msg)
            Utilities.writeLogFile(msg)
            return None

        # Update the GPS group to include a datasets for DATETAG and TIMETAG2
        for gp in root.groups:
            if gp.id.startswith("GP"):
                gpsGroup = gp
            # Need year-gang and sometimes Datetag from one of the sensors
            if gp.id.startswith("HSE"):
                esDateTag = gp.datasets["DATETAG"].columns["NONE"]
                esTimeTag2 = gp.datasets["TIMETAG2"].columns["NONE"]
                esSec = []
                for time in esTimeTag2:
                    esSec.append(Utilities.timeTag2ToSec(time))

        gpsGroup.addDataset("DATETAG")
        gpsGroup.addDataset("TIMETAG2")
        if "UTCPOS" in gpsGroup.datasets:
            gpsTime = gpsGroup.datasets["UTCPOS"].columns["NONE"]
        elif "TIME" in gpsGroup.datasets:
            # prepSAS output
            gpsTime = gpsGroup.datasets["TIME"].columns["UTC"]
        else:
            msg = 'Failed to import GPS data.'
            print(msg)
            Utilities.writeLogFile(msg)
            return None

        # Another case for GPGGA input...
        if gpsGroup.id.startswith("GPGGA"):
            # No date is provided in GPGGA, need to find nearest time in Es and take the Datetag from Es
            ''' Catch-22. In order to covert the gps time, we need the year and day, which GPGGA does not have.
                To get these, could compare to find the nearest DATETAG in Es. In order to compare the gps time
                to the Es time to find the nearest, I would need to convert them to datetimes ... which would
                require the year and day. Instead, I use either the first or last Datetag from Es, depending
                on whether UTC 00:00 was crossed.'''
            # If the date does not change in Es, then no problem, use the Datetag of Es first element.
            # Otherwise, change the datetag at midnight by one day
            gpsDateTag = []
            gpsTimeTag2 = []

            if esDateTag[0] != esDateTag[-1]:
                msg = "ProcessL1a.processL1a: Warning: File crosses UTC 00:00. Adjusting timestamps for matchup of Datetag."
                print(msg)
                Utilities.writeLogFile(msg)
                newDay = False
                for time in gpsTime:
                    gpsSec = Utilities.utcToSec(time)
                    if not 'gpsSecPrior' in locals():
                        gpsSecPrior = gpsSec
                    # Test for a change of ~24 hrs between this sample and the last sample
                    # To cross 0, gpsSecPrior would need to be approaching 86400 seconds
                    # In that case, take the final Es Datetag
                    if (gpsSecPrior - gpsSec) > 86000:
                        # Once triggered the first time, this will remain true for remainder of file
                        newDay = True
                    if newDay is True:
                        gpsDateTag.append(esDateTag[-1])
                        dtDate = Utilities.dateTagToDateTime(esDateTag[-1])
                        gpsTimeTag2.append(
                            Utilities.datetime2TimeTag2(
                                Utilities.utcToDateTime(dtDate, time)))
                    else:
                        gpsDateTag.append(esDateTag[0])
                        dtDate = Utilities.dateTagToDateTime(esDateTag[0])
                        gpsTimeTag2.append(
                            Utilities.datetime2TimeTag2(
                                Utilities.utcToDateTime(dtDate, time)))
                    gpsSecPrior = gpsSec
            else:
                for time in gpsTime:
                    gpsDateTag.append(esDateTag[0])
                    dtDate = Utilities.dateTagToDateTime(esDateTag[0])
                    gpsTimeTag2.append(
                        Utilities.datetime2TimeTag2(
                            Utilities.utcToDateTime(dtDate, time)))

            gpsGroup.datasets["DATETAG"].columns["NONE"] = gpsDateTag
            gpsGroup.datasets["TIMETAG2"].columns["NONE"] = gpsTimeTag2

        # Converts gp.columns to numpy array
        for gp in root.groups:
            if gp.id.startswith(
                    "SATMSG"):  # Don't convert these strings to datasets.
                for ds in gp.datasets.values():
                    ds.columnsToDataset()
            else:
                for ds in gp.datasets.values():
                    if not ds.columnsToDataset():
                        msg = "ProcessL1a.processL1a: Essential column cannot be converted to Dataset. Aborting."
                        print(msg)
                        Utilities.writeLogFile(msg)
                        return None

        # Apply SZA filter; Currently only works with SolarTracker data at L1A (again possible in L2)
        if ConfigFile.settings["bL1aCleanSZA"]:
            root.attributes['SZA_FILTER_L1A'] = ConfigFile.settings[
                "fL1aCleanSZAMax"]
            for gp in root.groups:
                # try:
                if 'FrameTag' in gp.attributes:
                    if gp.attributes["FrameTag"].startswith("SATNAV"):
                        elevData = gp.getDataset("ELEVATION")
                        elevation = elevData.data.tolist()
                        szaLimit = float(
                            ConfigFile.settings["fL1aCleanSZAMax"])
                        ''' It would be good to add local time as a printed output with SZA'''
                        if (90 - np.nanmax(elevation)) > szaLimit:
                            msg = f'SZA too low. Discarding entire file. {round(90-np.nanmax(elevation))}'
                            print(msg)
                            Utilities.writeLogFile(msg)
                            return None
                        else:
                            msg = f'SZA passed filter: {round(90-np.nanmax(elevation))}'
                            print(msg)
                            Utilities.writeLogFile(msg)
                else:
                    print(f'No FrameTag in {gp.id} group')

        return root
예제 #4
0
def readLUT(node, filename, headerlines):
    """
    Read in the M99 LUT for 550 nm

    Required arguments:
    node        = HDF5 Root object
    filename    = name of SeaBASS input file (string)
    headerlines = # of headerlines in the LUT text file

    LUT is in self.groups[0].datasets['LUT'].data

    phiView is the relative azimuth angle, and phiSun is 180 - phiView

    """

    node.id = 'rhoLUT'
    node.attributes['LUT origin'] = filename.split(os.path.sep)[-1]
    node.attributes[
        'Citation'] = 'Mobley, 1999, Appl Opt 38, page 7445, Eqn. 4'

    try:
        fileobj = open(filename, 'r')
    except Exception as e:
        print(f'Unable to open file for reading: {filename}. Error: {e}')
        return

    try:
        lines = fileobj.readlines()
        fileobj.close()
    except Exception as e:
        print(f'Unable to read data from file: {filename}. Error: {e}')
        return

    # Create the dictionary LUT
    lut = {
        'wind': [],
        'sza': [],
        'theta': [],
        'phiSun': [],
        'phiView': [],
        'rho': []
    }

    # Later, interpolate or find-nearest the field data to these steps to match...
    wind = []  # 0:2:14
    sza = []  # 0:10:80
    # I =[]       # 1:1:10
    # J = []      # 1:1:13
    theta = []  # 0:10:80, 87.5
    phiSun = []  # 0:15:180 # phi of 45 deg is relaz of 135 deg
    phiView = []  # 0:15:180
    rho = []  # result

    for i, line in enumerate(lines):
        if i < headerlines:
            continue
        data_row = re.search("WIND SPEED =", line)
        data_row = re.search("THETA_SUN", line)

        if data_row:
            elems = re.findall('\d+\.\d+', line)
            continue

        linefloat = [float(elem) for elem in line.split()]
        wind.append(float(elems[0]))
        sza.append(float(elems[1]))
        theta.append(linefloat[2])
        phiSun.append(linefloat[3])
        phiView.append(linefloat[4])
        rho.append(linefloat[5])

    lut['wind'] = wind
    lut['sza'] = sza
    lut['theta'] = theta
    lut['phiSun'] = phiSun
    lut['phiView'] = phiView
    lut['rho'] = rho

    gp = HDFGroup()
    gp.id = 'LUT'
    node.groups.append(gp)

    LUTDataset = gp.addDataset("LUT")
    LUTDataset.columns = lut
    LUTDataset.columnsToDataset()

    return node
예제 #5
0
    def processSplitLonFlag(filepath, dataDir, calibrationMap, startLongitude,
                            endLongitude, direction):

        print("Read: " + filepath)

        # gpsState: set to 1 if within start/end longitude, else 0
        gpsState = 0

        # Saves the starting and ending GPS HDF Group
        gpGPSStart = None
        gpGPSEnd = None

        # Saves the raw file header
        header = b""
        #msg = b""
        #message = b""

        # Index of start/end message block for start/end longitude
        iStart = -1
        iEnd = -1

        #(dirpath, filename) = os.path.split(filepath)
        #print(filename)

        #posframe = 1

        # Note: Prosoft adds posframe=1 to the GPS for some reason
        #print(contextMap.keys())
        #gpsGroup = contextMap["$GPRMC"]
        #ds = gpsGroup.getDataset("POSFRAME")
        #ds.appendColumn(u"COUNT", posframe)
        #posframe += 1

        # Start reading raw binary file
        with open(filepath, 'rb') as f:
            while 1:
                # Reads a block of binary data from file
                pos = f.tell()
                b = f.read(PreprocessRawFile.MAX_TAG_READ)
                f.seek(pos)

                if not b:
                    break

                #print b
                # Search for frame tag string in block
                for i in range(0, PreprocessRawFile.MAX_TAG_READ):
                    testString = b[i:].upper()
                    #print("test: ", testString[:6])

                    # Reset file position on max read (frame tag not found)
                    if i == PreprocessRawFile.MAX_TAG_READ - 1:
                        #f.read(PreprocessRawFile.MAX_TAG_READ)
                        f.read(PreprocessRawFile.RESET_TAG_READ)
                        break

                    # Reads header message type from frame tag
                    if testString.startswith(b"SATHDR"):
                        #print("SATHDR")
                        if i > 0:
                            f.read(i)
                        header += f.read(PreprocessRawFile.SATHDR_READ)

                        break
                    # Reads messages that starts with \$GPRMC and retrieves the CalibrationFile from map
                    else:
                        bytesRead = 0
                        for key in calibrationMap:
                            cf = calibrationMap[key]
                            if testString.startswith(
                                    b"$GPRMC") and testString.startswith(
                                        cf.id.upper().encode("utf-8")):
                                if i > 0:
                                    f.read(i)

                                # Read block from start of message
                                pos = f.tell()
                                msg = f.read(PreprocessRawFile.MAX_BLOCK_READ)
                                f.seek(pos)

                                # Convert message to HDFGroup
                                gp = HDFGroup()
                                bytesRead = cf.convertRaw(msg, gp)

                                # Read till end of message
                                if bytesRead >= 0:
                                    f.read(bytesRead)
                                    #if gpsState == 0:
                                    #    msg = f.read(bytesRead)
                                    #else:
                                    #    msg += f.read(bytesRead)

                                #gp.printd()
                                if gp.getDataset("LONPOS"):
                                    #print("has gps")
                                    lonData = gp.getDataset("LONPOS")
                                    lonHemiData = gp.getDataset("LONHEMI")
                                    lonDM = lonData.columns["NONE"][0]
                                    lonDirection = lonHemiData.columns["NONE"][
                                        0]
                                    longitude = Utilities.dmToDd(
                                        lonDM, lonDirection)
                                    #print(longitude)
                                    # Detect if we are in specified longitude
                                    if longitude > startLongitude and longitude < endLongitude:
                                        if gpsState == 0:
                                            iStart = pos
                                            gpGPSStart = gp
                                        else:
                                            iEnd = f.tell()
                                            gpGPSEnd = gp
                                        #message += msg
                                        gpsState = 1
                                    # Not within start/end longitude
                                    else:
                                        if gpsState == 1:
                                            #print("Test")
                                            PreprocessRawFile.createRawFile(
                                                dataDir, gpGPSStart, gpGPSEnd,
                                                direction, f, header, iStart,
                                                iEnd)
                                        gpsState = 0

                                break
                        if bytesRead > 0:
                            break
            # In case file finished processing without reaching endLongitude
            if gpsState == 1:
                if gpGPSStart is not None and gpGPSEnd is not None:
                    PreprocessRawFile.createRawFile(dataDir, gpGPSStart,
                                                    gpGPSEnd, direction, f,
                                                    header, iStart, iEnd)
예제 #6
0
    def processSplitTimer(filepath, dataDir, calibrationMap):

        print("Split Timer: " + filepath)

        # Used to detect when the timer is reset
        saveTime = -1

        # Saves the starting and ending GPS HDF Group
        timerStart = None
        timerEnd = None

        # Saves the raw file header
        header = b""

        # Index of start/end message block for start/end timer
        iStart = -1
        iEnd = -1

        # Start reading raw binary file
        with open(filepath, 'rb') as f:
            while 1:
                # Reads a block of binary data from file
                pos = f.tell()
                b = f.read(PreprocessRawFile.MAX_TAG_READ)
                f.seek(pos)

                if not b:
                    break

                #print b
                # Search for frame tag string in block
                for i in range(0, PreprocessRawFile.MAX_TAG_READ):
                    testString = b[i:].upper()

                    # Reset file position on max read (frame tag not found)
                    if i == PreprocessRawFile.MAX_TAG_READ - 1:
                        f.read(PreprocessRawFile.RESET_TAG_READ)
                        break

                    # Reads header message type from frame tag
                    if testString.startswith(b"SATHDR"):
                        #print("SATHDR")
                        if i > 0:
                            f.read(i)
                        header += f.read(PreprocessRawFile.SATHDR_READ)

                        break
                    # Reads messages that starts with \$GPRMC and retrieves the CalibrationFile from map
                    else:
                        bytesRead = 0
                        for key in calibrationMap:
                            cf = calibrationMap[key]
                            if testString.startswith(
                                    cf.id.upper().encode("utf-8")):
                                if i > 0:
                                    f.read(i)

                                # Read block from start of message
                                pos = f.tell()
                                msg = f.read(PreprocessRawFile.MAX_BLOCK_READ)
                                f.seek(pos)

                                # Convert message to HDFGroup
                                gp = HDFGroup()
                                bytesRead = cf.convertRaw(msg, gp)

                                # Read till end of message
                                if bytesRead >= 0:
                                    f.read(bytesRead)

                                #gp.printd()
                                if gp.getDataset("ES") and gp.getDataset(
                                        "TIMER"):
                                    #print("has gps")
                                    timerData = gp.getDataset("TIMER")
                                    time = timerData.columns["NONE"][0]
                                    #print(time)
                                    # Detect if we are in specified longitude
                                    if time < saveTime:
                                        PreprocessRawFile.createRawFileTimer(
                                            dataDir, timerStart, timerEnd, f,
                                            header, iStart, iEnd)
                                        timerStart = gp
                                        iStart = pos
                                        saveTime = time
                                    else:
                                        if saveTime == -1:
                                            timerStart = gp
                                            iStart = pos
                                        iEnd = f.tell()
                                        timerEnd = gp
                                        saveTime = time
                                break
                        if bytesRead > 0:
                            break
예제 #7
0
    def cleanSunAngle(filepath,
                      calibrationMap,
                      angleMin,
                      angleMax,
                      rotatorHomeAngle=0):
        print("Clean Raw File")

        header = b""
        message = b""

        # Index of start/end message block for start/end longitude
        iStart = 0
        iEnd = 0

        # Start reading raw binary file
        with open(filepath, 'rb') as f:
            while 1:
                # Reads a block of binary data from file
                pos = f.tell()
                b = f.read(PreprocessRawFile.MAX_TAG_READ)
                f.seek(pos)

                if not b:
                    break

                #print b
                # Search for frame tag string in block
                for i in range(0, PreprocessRawFile.MAX_TAG_READ):
                    testString = b[i:].upper()
                    #print("test: ", testString[:6])

                    # Reset file position on max read (frame tag not found)
                    if i == PreprocessRawFile.MAX_TAG_READ - 1:
                        #f.read(PreprocessRawFile.MAX_TAG_READ)
                        f.read(PreprocessRawFile.RESET_TAG_READ)
                        break

                    # Reads header message type from frame tag
                    if testString.startswith(b"SATHDR"):
                        #print("SATHDR")
                        if i > 0:
                            f.read(i)
                        header += f.read(PreprocessRawFile.SATHDR_READ)

                        break
                    # Reads messages that starts with SATNAV and retrieves the CalibrationFile from map
                    else:
                        bytesRead = 0
                        for key in calibrationMap:
                            cf = calibrationMap[key]
                            if testString.startswith(
                                    b"SATNAV") and testString.startswith(
                                        cf.id.upper().encode("utf-8")):
                                if i > 0:
                                    f.read(i)

                                # Read block from start of message
                                pos = f.tell()
                                msg = f.read(PreprocessRawFile.MAX_BLOCK_READ)
                                f.seek(pos)

                                # Convert message to HDFGroup
                                gp = HDFGroup()
                                bytesRead = cf.convertRaw(msg, gp)

                                # Read till end of message
                                if bytesRead >= 0:
                                    f.read(bytesRead)

                                #gp.printd()

                                if gp.getDataset("AZIMUTH") and gp.getDataset(
                                        "HEADING") and gp.getDataset(
                                            "POINTING"):
                                    angle = 0

                                    azimuthData = gp.getDataset("AZIMUTH")
                                    azimuth = azimuthData.columns["SUN"][0]

                                    #headingData = gp.getDataset("HEADING")
                                    #sasTrue = headingData.columns["SAS_TRUE"][0]
                                    #angle = PreprocessRawFile.normalizeAngle(azimuth - sasTrue)
                                    #print("azimuth - sasTrue: ", PreprocessRawFile.normalizeAngle(azimuth - sasTrue))

                                    pointingData = gp.getDataset("POINTING")
                                    rotatorAngle = pointingData.columns[
                                        "ROTATOR"][0]

                                    headingData = gp.getDataset("HEADING")
                                    shipTrue = headingData.columns[
                                        "SHIP_TRUE"][0]

                                    #angle = PreprocessRawFile.normalizeAngle(shipTrue + rotatorHomeAngle)
                                    #angle = PreprocessRawFile.normalizeAngle(angle - rotatorAngle)
                                    #angle = PreprocessRawFile.normalizeAngle(azimuth - angle)
                                    angle = PreprocessRawFile.normalizeAngle(
                                        rotatorAngle + shipTrue - azimuth +
                                        270 - 35 + rotatorHomeAngle)
                                    #print("Angle: ", angle)

                                    #print("Angle: ", angle)
                                    #if angle >= 90 and angle <= 135:
                                    if angle >= angleMin and angle <= angleMax:
                                        # iStart is -1 if previous SATNAV was bad angle
                                        if iStart != -1:
                                            # Append measurements from previous block to new file
                                            iEnd = f.tell()

                                            pos = f.tell()
                                            f.seek(iStart)
                                            msg = f.read(iEnd - iStart)
                                            f.seek(pos)

                                            message += msg
                                            iStart = f.tell()
                                        else:
                                            # Reset iStart to start of current SATNAV
                                            iStart = f.tell() - bytesRead

                                    else:
                                        # Found bad angles, set iStart to -1 to ignore raw data
                                        print("Skip Bad Angle: ", angle)
                                        iStart = -1

                                break
                        if bytesRead > 0:
                            break
            if iStart != -1:
                f.seek(iStart)
                message += f.read()

        # Write file
        with open(filepath, 'wb') as fout:
            fout.write(message)
예제 #8
0
    def cleanRotatorAngle(filepath,
                          calibrationMap,
                          angleMin,
                          angleMax,
                          rotatorHomeAngle=0,
                          rotatorDelay=60):
        print("Clean Raw File")

        header = b""
        message = b""

        # Index of start/end message block for start/end longitude
        iStart = 0
        iEnd = 0

        init = False
        saveTime = 0
        saveAngle = 0

        # Start reading raw binary file
        with open(filepath, 'rb') as f:
            while 1:
                # Reads a block of binary data from file
                pos = f.tell()
                b = f.read(PreprocessRawFile.MAX_TAG_READ)
                f.seek(pos)

                if not b:
                    break

                #print b
                # Search for frame tag string in block
                for i in range(0, PreprocessRawFile.MAX_TAG_READ):
                    testString = b[i:].upper()
                    #print("test: ", testString[:6])

                    # Reset file position on max read (frame tag not found)
                    if i == PreprocessRawFile.MAX_TAG_READ - 1:
                        #f.read(PreprocessRawFile.MAX_TAG_READ)
                        f.read(PreprocessRawFile.RESET_TAG_READ)
                        break

                    # Reads header message type from frame tag
                    if testString.startswith(b"SATHDR"):
                        #print("SATHDR")
                        if i > 0:
                            f.read(i)
                        header += f.read(PreprocessRawFile.SATHDR_READ)

                        break
                    # Reads messages that starts with SATNAV and retrieves the CalibrationFile from map
                    else:
                        bytesRead = 0
                        for key in calibrationMap:
                            cf = calibrationMap[key]
                            if testString.startswith(
                                    b"SATNAV") and testString.startswith(
                                        cf.id.upper().encode("utf-8")):
                                if i > 0:
                                    f.read(i)

                                # Read block from start of message
                                pos = f.tell()
                                msg = f.read(PreprocessRawFile.MAX_BLOCK_READ)
                                f.seek(pos)

                                # Convert message to HDFGroup
                                gp = HDFGroup()
                                bytesRead = cf.convertRaw(msg, gp)

                                # Read till end of message
                                if bytesRead >= 0:
                                    f.read(bytesRead)

                                #gp.printd()

                                if gp.getDataset("AZIMUTH") and gp.getDataset(
                                        "HEADING") and gp.getDataset(
                                            "POINTING"):

                                    pointingData = gp.getDataset("POINTING")
                                    #angle = pointingData.columns["ROTATOR"][0] - rotatorHomeAngle
                                    angle = pointingData.columns["ROTATOR"][0]

                                    timetag2Data = gp.getDataset("TIMETAG2")
                                    time = Utilities.timeTag2ToSec(
                                        timetag2Data.columns["NONE"][0])

                                    if not init:
                                        saveAngle = angle
                                        saveTime = time
                                        init = True
                                    else:
                                        # Detect angle changed
                                        if saveAngle < angle + 0.05 and saveAngle > angle - 0.05:
                                            saveAngle = angle
                                        else:
                                            #print("Angle:", saveAngle, angle, "Time:", time)
                                            saveAngle = angle
                                            saveTime = time
                                            # Tell program to ignore angles until rotatorDelay time
                                            angle = angleMin - 1

                                    #print("Angle: ", angle)
                                    #if angle >= 90 and angle <= 135:
                                    #if angle >= angleMin and angle <= angleMax:
                                    if angle >= angleMin and angle <= angleMax and time > saveTime + rotatorDelay:
                                        # iStart is -1 if previous SATNAV was bad angle
                                        if iStart != -1:
                                            # Append measurements from previous block to new file
                                            iEnd = f.tell()

                                            pos = f.tell()
                                            f.seek(iStart)
                                            msg = f.read(iEnd - iStart)
                                            f.seek(pos)

                                            message += msg
                                            iStart = f.tell()
                                        else:
                                            # Reset iStart to start of current SATNAV
                                            iStart = f.tell() - bytesRead

                                    else:
                                        # Found bad angles, set iStart to -1 to ignore raw data
                                        if time < saveTime + rotatorDelay:
                                            print(
                                                "Rotator Angle Changed, Time: ",
                                                time)
                                        else:
                                            print("Skip Rotator Angle: ",
                                                  angle)
                                        iStart = -1

                                break
                        if bytesRead > 0:
                            break
            if iStart != -1:
                f.seek(iStart)
                message += f.read()

        # Write file
        with open(filepath, 'wb') as fout:
            fout.write(message)