Example #1
0
class TestPOD(unittest.TestCase):
    """Test the POD GAC reader."""

    longMessage = True

    def setUp(self):
        """Set up the test."""
        self.reader = GACPODReader()
        # python 2 compatibility
        if sys.version_info.major < 3:
            self.assertRaisesRegex = self.assertRaisesRegexp

    def test__validate_header(self):
        """Test the header validation"""
        filename = b'NSS.GHRR.TN.D80001.S0332.E0526.B0627173.WI'
        head = {'data_set_name': filename}
        GACPODReader._validate_header(head)
        # wrong name pattern
        with self.assertRaisesRegex(ReaderError,
                                    'Data set name .* does not match!'):
            head = {'data_set_name': b'abc.txt'}
            GACPODReader._validate_header(head)
        # wrong platform
        name = b'NSS.GHRR.NL.D02187.S1904.E2058.B0921517.GC'
        with self.assertRaisesRegex(ReaderError,
                                    'Improper platform id "NL"!'):
            head = {'data_set_name': name}
            GACPODReader._validate_header(head)
        # wrong transfer mode
        name = filename.replace(b'GHRR', b'LHRR')
        with self.assertRaisesRegex(ReaderError,
                                    'Improper transfer mode "LHRR"!'):
            head = {'data_set_name': name}
            GACPODReader._validate_header(head)
        # other change reader
        head = {'data_set_name': name}
        LACPODReader._validate_header(head)

    @mock.patch('pygac.reader.Reader.get_calibrated_channels')
    def test__get_calibrated_channels_uniform_shape(self, get_channels):
        """Test the uniform shape as required by gac_io.save_gac."""
        channels = np.arange(2*2*5, dtype=float).reshape((2, 2, 5))
        get_channels.return_value = channels
        uniform_channels = self.reader._get_calibrated_channels_uniform_shape()
        self.assertTrue(np.isnan(uniform_channels[:, :, 2]).all())
        self.assertTrue(uniform_channels[:, :, [0, 1, 3, 4, 5]].sum()
                        == channels.sum())

    def test_decode_timestamps(self):
        """Test POD timestamp decoding."""
        # Reference timestamps, one before 2000 one after 2000
        t2000_ref = (2001, 335, 53644260)
        t1900_ref = (1983, 336, 35058207)

        # Encoded version
        t2000_enc = np.array([847, 818, 35812])
        t1900_enc = np.array([42832, 534, 61983])

        # Test whether PODReader decodes them correctly
        self.assertEqual(GACPODReader.decode_timestamps(t2000_enc), t2000_ref,
                         msg='Timestamp after 2000 was decoded incorrectly')
        self.assertEqual(GACPODReader.decode_timestamps(t1900_enc), t1900_ref,
                         msg='Timestamp before 2000 was decoded incorrectly')

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_header_timestamp(self, decode_timestamps):
        """Test readout of header timestamp."""
        self.reader.head = {'start_time': 123}
        decode_timestamps.return_value = np.array(
            [2019]), np.array([123]), np.array([123456])
        time = self.reader.get_header_timestamp()
        decode_timestamps.assert_called_with(123)
        self.assertEqual(time, dt.datetime(2019, 5, 3, 0, 2, 3, 456000))

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_times(self, decode_timestamps):
        """Test getting times."""
        self.reader.scans = {'time_code': 123}
        self.reader._get_times()
        decode_timestamps.assert_called_with(123)

    def test_get_lonlat(self):
        """Test readout of lon/lat coordinates."""
        earth_loc = 128 * np.array([[1, 2, 3, 4],
                                    [5, 6, 7, 8]])
        self.reader.scans = {'earth_location': earth_loc}

        lons_exp = np.array([[2, 4],
                             [6, 8]])
        lats_exp = np.array([[1, 3],
                             [5, 7]])

        lons, lats = self.reader._get_lonlat()
        numpy.testing.assert_array_equal(lons, lons_exp)
        numpy.testing.assert_array_equal(lats, lats_exp)

    @mock.patch('pygac.pod_reader.get_tsm_idx')
    def test_get_tsm_pixels(self, get_tsm_idx):
        """Test channel set used for TSM correction."""
        ones = np.ones((409, 100))
        zeros = np.zeros(ones.shape)
        ch1 = 1*ones
        ch2 = 2*ones
        ch4 = 4*ones
        ch5 = 5*ones
        channels = np.dstack((ch1, ch2, zeros, ch4, ch5))
        self.reader.get_tsm_pixels(channels)
        get_tsm_idx.assert_called_with(CalledWithArray(ch1),
                                       CalledWithArray(ch2),
                                       CalledWithArray(ch4),
                                       CalledWithArray(ch5))
Example #2
0
class TestPOD(unittest.TestCase):
    """Test the POD GAC reader."""

    longMessage = True

    def setUp(self):
        """Set up the test."""
        self.reader = GACPODReader()
        # python 2 compatibility
        if sys.version_info.major < 3:
            self.assertRaisesRegex = self.assertRaisesRegexp

    def test__validate_header(self):
        """Test the header validation"""
        filename = b'NSS.GHRR.TN.D80001.S0332.E0526.B0627173.WI'
        head = {'data_set_name': filename}
        GACPODReader._validate_header(head)
        # wrong name pattern
        with self.assertRaisesRegex(ReaderError,
                                    'Data set name .* does not match!'):
            head = {'data_set_name': b'abc.txt'}
            GACPODReader._validate_header(head)
        # wrong platform
        name = b'NSS.GHRR.NL.D02187.S1904.E2058.B0921517.GC'
        with self.assertRaisesRegex(ReaderError, 'Improper platform id "NL"!'):
            head = {'data_set_name': name}
            GACPODReader._validate_header(head)
        # wrong transfer mode
        name = filename.replace(b'GHRR', b'LHRR')
        with self.assertRaisesRegex(ReaderError,
                                    'Improper transfer mode "LHRR"!'):
            head = {'data_set_name': name}
            GACPODReader._validate_header(head)
        # other change reader
        head = {'data_set_name': name}
        LACPODReader._validate_header(head)

    @mock.patch('pygac.reader.Reader.get_calibrated_channels')
    def test__get_calibrated_channels_uniform_shape(self, get_channels):
        """Test the uniform shape as required by gac_io.save_gac."""
        channels = np.arange(2 * 2 * 5, dtype=float).reshape((2, 2, 5))
        get_channels.return_value = channels
        uniform_channels = self.reader._get_calibrated_channels_uniform_shape()
        self.assertTrue(np.isnan(uniform_channels[:, :, 2]).all())
        self.assertTrue(
            uniform_channels[:, :, [0, 1, 3, 4, 5]].sum() == channels.sum())

    def test_decode_timestamps(self):
        """Test POD timestamp decoding."""
        # Reference timestamps, one before 2000 one after 2000
        t2000_ref = (2001, 335, 53644260)
        t1900_ref = (1983, 336, 35058207)

        # Encoded version
        t2000_enc = np.array([847, 818, 35812])
        t1900_enc = np.array([42832, 534, 61983])

        # Test whether PODReader decodes them correctly
        self.assertEqual(GACPODReader.decode_timestamps(t2000_enc),
                         t2000_ref,
                         msg='Timestamp after 2000 was decoded incorrectly')
        self.assertEqual(GACPODReader.decode_timestamps(t1900_enc),
                         t1900_ref,
                         msg='Timestamp before 2000 was decoded incorrectly')

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_header_timestamp(self, decode_timestamps):
        """Test readout of header timestamp."""
        self.reader.head = {'start_time': 123}
        decode_timestamps.return_value = np.array([2019]), np.array(
            [123]), np.array([123456])
        time = self.reader.get_header_timestamp()
        decode_timestamps.assert_called_with(123)
        self.assertEqual(time, dt.datetime(2019, 5, 3, 0, 2, 3, 456000))

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_times(self, decode_timestamps):
        """Test getting times."""
        self.reader.scans = {'time_code': 123}
        self.reader._get_times()
        decode_timestamps.assert_called_with(123)

    def test_get_lonlat(self):
        """Test readout of lon/lat coordinates."""
        earth_loc = 128 * np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
        self.reader.scans = {'earth_location': earth_loc}

        lons_exp = np.array([[2, 4], [6, 8]])
        lats_exp = np.array([[1, 3], [5, 7]])

        lons, lats = self.reader._get_lonlat()
        numpy.testing.assert_array_equal(lons, lons_exp)
        numpy.testing.assert_array_equal(lats, lats_exp)

    @mock.patch('pygac.pod_reader.get_tsm_idx')
    def test_get_tsm_pixels(self, get_tsm_idx):
        """Test channel set used for TSM correction."""
        ones = np.ones((409, 100))
        zeros = np.zeros(ones.shape)
        ch1 = 1 * ones
        ch2 = 2 * ones
        ch4 = 4 * ones
        ch5 = 5 * ones
        channels = np.dstack((ch1, ch2, zeros, ch4, ch5))
        self.reader.get_tsm_pixels(channels)
        get_tsm_idx.assert_called_with(CalledWithArray(ch1),
                                       CalledWithArray(ch2),
                                       CalledWithArray(ch4),
                                       CalledWithArray(ch5))

    def test_quality_indicators(self):
        """Test the quality indicator unpacking."""
        reader = self.reader
        QFlag = reader.QFlag
        quality_indicators = np.array(
            [
                0,  # nothing flagged
                -1,  # everything flagged
                QFlag.CALIBRATION | QFlag.NO_EARTH_LOCATION,
                QFlag.TIME_ERROR | QFlag.DATA_GAP,
                QFlag.FATAL_FLAG
            ],
            dtype=np.uint32)
        reader.scans = {
            self.reader._quality_indicators_key: quality_indicators
        }
        # test mask, i.e. QFlag.FATAL_FLAG | QFlag.CALIBRATION | QFlag.NO_EARTH_LOCATION
        expected_mask = np.array([False, True, True, False, True], dtype=bool)
        numpy.testing.assert_array_equal(reader.mask, expected_mask)
        # test individual flags
        self.assertTrue(reader._get_corrupt_mask(flags=QFlag.FATAL_FLAG).any())
        # count the occurence (everything flagged and last entrance => 2)
        self.assertEqual(
            reader._get_corrupt_mask(flags=QFlag.FATAL_FLAG).sum(), 2)

    @mock.patch('pygac.pod_reader.get_lonlatalt')
    @mock.patch('pygac.pod_reader.compute_pixels')
    @mock.patch('pygac.reader.Reader.get_tle_lines')
    @mock.patch('pygac.pod_reader.avhrr_gac')
    def test__adjust_clock_drift(self, avhrr_gac, get_tle_lines,
                                 compute_pixels, get_lonlatalt):
        """Test the clock drift adjustment."""
        sat_name = "fake_sat"
        reader = self.reader

        # We construct the following input
        # the scan lines do not start with 1 and have a gap
        scan_lines = np.array([15, 16, 17, 18, 22, 23, 24, 25])
        scan_rate = 0.5  # seconds/line
        # the first valid scans starts 1980-01-01 (without clock correction)
        # which leads to the following utcs for the given scan lines
        # ['1980-01-01T00:00:00.000', '1980-01-01T00:00:00.500',
        #  '1980-01-01T00:00:01.000', '1980-01-01T00:00:01.500',
        #  '1980-01-01T00:00:03.500', '1980-01-01T00:00:04.000',
        #  '1980-01-01T00:00:04.500', '1980-01-01T00:00:05.000']
        scan_utcs = ((1000 * scan_rate *
                      (scan_lines - scan_lines[0])).astype('timedelta64[ms]') +
                     np.datetime64("1980", "ms"))
        # For the geolocations, we assume an artificial swath of two pixels width
        # from north to south with constant lon, lat difference of 3deg for simplicity
        # lons = [[0, 3], [0, 3], [0, 3], [0, 3],
        #         [0, 3], [0, 3], [0, 3], [0, 3]],
        # lats = [[45, 45], [48, 48], [51, 51], [54, 54],
        #         [66, 66], [69, 69], [72, 72], [75, 75]]
        scan_angle = 3.0  # deg
        scan_lons, scan_lats = np.meshgrid(scan_angle * np.arange(2),
                                           scan_angle * scan_lines)

        # we assume a constant clock offset of 3.75 seconds
        # which should lead to the following adjustment on utcs
        # ['1979-12-31T23:59:56.250', '1979-12-31T23:59:56.750',
        #  '1979-12-31T23:59:57.250', '1979-12-31T23:59:57.750',
        #  '1979-12-31T23:59:59.750', '1980-01-01T00:00:00.250',
        #  '1980-01-01T00:00:00.750', '1980-01-01T00:00:01.250']
        offset = 3.75
        scan_offsets = offset * np.ones_like(scan_lines,
                                             dtype=float)  # seconds
        expected_utcs = scan_utcs - (1000 *
                                     scan_offsets).astype('timedelta64[ms]')

        # the adjustment of geolocations should keep the lons unchanged,
        # but should shift the lats by scan_angel * offset / scan_rate
        # = 3deg/line * 3.75sec / 0.5sec/line = 22.5deg
        # [[22.5, 22.5], [25.5, 25.5], [28.5, 28.5], [31.5, 31.5],
        #  [43.5, 43.5], [46.5, 46.5], [49.5, 49.5], [52.5, 52.5]]
        expected_lons = scan_lons
        lats_shift = scan_angle * offset / scan_rate
        expected_lats = scan_lats - lats_shift

        # prepare the reader
        reader.scans = {"scan_line_number": scan_lines}
        reader.utcs = scan_utcs
        reader.lons = scan_lons
        reader.lats = scan_lats
        reader.spacecraft_name = sat_name

        # prepare offsets
        clock_offsets_txt[sat_name] = ("75001 000000 {offset}  "
                                       "85001 000000 {offset}").format(
                                           offset=offset)
        # set attitude coeffs
        reader._rpy = np.zeros(3)

        # set mocks for reader._compute_missing_lonlat call
        sgeom = mock.Mock()
        sgeom.times.return_value = [None]
        avhrr_gac.return_value = sgeom
        get_tle_lines.return_value = [None, None]
        compute_pixels.return_value = None
        # for the following mock, we need to know the missing values in advanced,
        # which we do, because we can convert the offset seconds into line number.
        offset_lines = offset / scan_rate
        min_line = np.floor(scan_lines[0] - offset_lines).astype(int)
        max_line = scan_lines[-1]
        missed_lines = np.setdiff1d(np.arange(min_line, max_line + 1),
                                    scan_lines)
        n_missed = len(missed_lines)
        missed_lons = np.tile([0., scan_angle], n_missed)
        missed_lats = np.repeat(scan_angle * missed_lines, 2)
        get_lonlatalt.return_value = [missed_lons, missed_lats]

        # adjust clock drift
        reader._adjust_clock_drift()

        # check output
        # use allclose for geolocations, because the slerp interpolation
        # includes a transormation to cartesian coordinates and back to lon, lats.
        numpy.testing.assert_array_equal(reader.utcs, expected_utcs)
        numpy.testing.assert_allclose(reader.lons, expected_lons)
        numpy.testing.assert_allclose(reader.lats, expected_lats)

        # undo changes to clock_offsets_txt
        clock_offsets_txt.pop(sat_name)
Example #3
0
class TestPOD(unittest.TestCase):
    """Test the POD GAC reader."""

    longMessage = True

    def setUp(self):
        """Set up the test."""
        self.reader = GACPODReader()

    def test_decode_timestamps(self):
        """Test POD timestamp decoding."""
        # Reference timestamps, one before 2000 one after 2000
        t2000_ref = (2001, 335, 53644260)
        t1900_ref = (1983, 336, 35058207)

        # Encoded version
        t2000_enc = np.array([847, 818, 35812])
        t1900_enc = np.array([42832, 534, 61983])

        # Test whether PODReader decodes them correctly
        self.assertEqual(GACPODReader.decode_timestamps(t2000_enc),
                         t2000_ref,
                         msg='Timestamp after 2000 was decoded incorrectly')
        self.assertEqual(GACPODReader.decode_timestamps(t1900_enc),
                         t1900_ref,
                         msg='Timestamp before 2000 was decoded incorrectly')

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_header_timestamp(self, decode_timestamps):
        """Test readout of header timestamp."""
        self.reader.head = {'start_time': 123}
        decode_timestamps.return_value = np.array([2019]), np.array(
            [123]), np.array([123456])
        time = self.reader.get_header_timestamp()
        decode_timestamps.assert_called_with(123)
        self.assertEqual(time, dt.datetime(2019, 5, 3, 0, 2, 3, 456000))

    @mock.patch('pygac.gac_pod.GACPODReader.decode_timestamps')
    def test_get_times(self, decode_timestamps):
        """Test getting times."""
        self.reader.scans = {'time_code': 123}
        self.reader._get_times()
        decode_timestamps.assert_called_with(123)

    def test_get_lonlat(self):
        """Test readout of lon/lat coordinates."""
        earth_loc = 128 * np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
        self.reader.scans = {'earth_location': earth_loc}

        lons_exp = np.array([[2, 4], [6, 8]])
        lats_exp = np.array([[1, 3], [5, 7]])

        lons, lats = self.reader._get_lonlat()
        numpy.testing.assert_array_equal(lons, lons_exp)
        numpy.testing.assert_array_equal(lats, lats_exp)

    @mock.patch('pygac.pod_reader.get_tsm_idx')
    def test_get_tsm_pixels(self, get_tsm_idx):
        """Test channel set used for TSM correction."""
        ones = np.ones((409, 100))
        zeros = np.zeros(ones.shape)
        ch1 = 1 * ones
        ch2 = 2 * ones
        ch4 = 4 * ones
        ch5 = 5 * ones
        channels = np.dstack((ch1, ch2, zeros, ch4, ch5))
        self.reader.get_tsm_pixels(channels)
        get_tsm_idx.assert_called_with(CalledWithArray(ch1),
                                       CalledWithArray(ch2),
                                       CalledWithArray(ch4),
                                       CalledWithArray(ch5))