LCOV - code coverage report
Current view: directory - frmts/l1b - l1bdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 724 505 69.8 %
Date: 2011-12-18 Functions: 27 20 74.1 %

       1                 : /******************************************************************************
       2                 :  * $Id: l1bdataset.cpp 22715 2011-07-12 10:34:07Z rouault $
       3                 :  *
       4                 :  * Project:  NOAA Polar Orbiter Level 1b Dataset Reader (AVHRR)
       5                 :  * Purpose:  Can read NOAA-9(F)-NOAA-17(M) AVHRR datasets
       6                 :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       7                 :  *
       8                 :  * Some format info at: http://www.sat.dundee.ac.uk/noaa1b.html
       9                 :  *
      10                 :  ******************************************************************************
      11                 :  * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
      12                 :  *
      13                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      14                 :  * copy of this software and associated documentation files (the "Software"),
      15                 :  * to deal in the Software without restriction, including without limitation
      16                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17                 :  * and/or sell copies of the Software, and to permit persons to whom the
      18                 :  * Software is furnished to do so, subject to the following conditions:
      19                 :  *
      20                 :  * The above copyright notice and this permission notice shall be included
      21                 :  * in all copies or substantial portions of the Software.
      22                 :  *
      23                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29                 :  * DEALINGS IN THE SOFTWARE.
      30                 :  ****************************************************************************/
      31                 : 
      32                 : #include "gdal_pam.h"
      33                 : #include "cpl_string.h"
      34                 : 
      35                 : CPL_CVSID("$Id: l1bdataset.cpp 22715 2011-07-12 10:34:07Z rouault $");
      36                 : 
      37                 : CPL_C_START
      38                 : void    GDALRegister_L1B(void);
      39                 : CPL_C_END
      40                 : 
      41                 : enum {                  // File formats
      42                 :     L1B_NONE,           // Not a L1B format
      43                 :     L1B_NOAA9,          // NOAA-9/14
      44                 :     L1B_NOAA15,         // NOAA-15/METOP-2
      45                 :     L1B_NOAA15_NOHDR    // NOAA-15/METOP-2 without ARS header
      46                 : };
      47                 : 
      48                 : enum {          // Spacecrafts:
      49                 :     TIROSN,     // TIROS-N
      50                 :     NOAA6,      // NOAA-6(A)
      51                 :     NOAAB,      // NOAA-B
      52                 :     NOAA7,      // NOAA-7(C)
      53                 :     NOAA8,      // NOAA-8(E)
      54                 :     NOAA9,      // NOAA-9(F)
      55                 :     NOAA10,     // NOAA-10(G)
      56                 :     NOAA11,     // NOAA-11(H)
      57                 :     NOAA12,     // NOAA-12(D)
      58                 :     NOAA13,     // NOAA-13(I)
      59                 :     NOAA14,     // NOAA-14(J)
      60                 :     NOAA15,     // NOAA-15(K)
      61                 :     NOAA16,     // NOAA-16(L)
      62                 :     NOAA17,     // NOAA-17(M)
      63                 :     NOAA18,     // NOAA-18(N)
      64                 :     METOP2      // METOP-2(A)
      65                 : };
      66                 : 
      67                 : enum {          // Product types
      68                 :     HRPT,
      69                 :     LAC,
      70                 :     GAC,
      71                 :     FRAC
      72                 : };
      73                 : 
      74                 : enum {          // Data format
      75                 :     PACKED10BIT,
      76                 :     UNPACKED8BIT,
      77                 :     UNPACKED16BIT
      78                 : };
      79                 : 
      80                 : enum {          // Receiving stations names:
      81                 :     DU,         // Dundee, Scotland, UK
      82                 :     GC,         // Fairbanks, Alaska, USA (formerly Gilmore Creek)
      83                 :     HO,         // Honolulu, Hawaii, USA
      84                 :     MO,         // Monterey, California, USA
      85                 :     WE,         // Western Europe CDA, Lannion, France
      86                 :     SO,         // SOCC (Satellite Operations Control Center), Suitland, Maryland, USA
      87                 :     WI,         // Wallops Island, Virginia, USA
      88                 :     SV,         // Svalbard, Norway
      89                 :     UNKNOWN_STATION
      90                 : };
      91                 : 
      92                 : enum {          // Data processing centers:
      93                 :     CMS,        // Centre de Meteorologie Spatiale - Lannion, France
      94                 :     DSS,        // Dundee Satellite Receiving Station - Dundee, Scotland, UK
      95                 :     NSS,        // NOAA/NESDIS - Suitland, Maryland, USA
      96                 :     UKM,        // United Kingdom Meteorological Office - Bracknell, England, UK
      97                 :     UNKNOWN_CENTER
      98                 : };
      99                 : 
     100                 : enum {          // AVHRR Earth location indication
     101                 :     ASCEND,
     102                 :     DESCEND
     103                 : };
     104                 : 
     105                 : /************************************************************************/
     106                 : /*                      AVHRR band widths                               */
     107                 : /************************************************************************/
     108                 : 
     109                 : static const char *apszBandDesc[] =
     110                 : {
     111                 :     // NOAA-7 -- METOP-2 channels
     112                 :     "AVHRR Channel 1:  0.58  micrometers -- 0.68 micrometers",
     113                 :     "AVHRR Channel 2:  0.725 micrometers -- 1.10 micrometers",
     114                 :     "AVHRR Channel 3:  3.55  micrometers -- 3.93 micrometers",
     115                 :     "AVHRR Channel 4:  10.3  micrometers -- 11.3 micrometers",
     116                 :     "AVHRR Channel 5:  11.5  micrometers -- 12.5 micrometers",  // not in NOAA-6,-8,-10
     117                 :     // NOAA-13
     118                 :     "AVHRR Channel 5:  11.4  micrometers -- 12.4 micrometers",
     119                 :     // NOAA-15 -- METOP-2
     120                 :     "AVHRR Channel 3A: 1.58  micrometers -- 1.64 micrometers",
     121                 :     "AVHRR Channel 3B: 3.55  micrometers -- 3.93 micrometers"
     122                 :     };
     123                 : 
     124                 : /************************************************************************/
     125                 : /*      L1B file format related constants                               */
     126                 : /************************************************************************/
     127                 : 
     128                 : #define L1B_DATASET_NAME_SIZE       42  // Length of the string containing
     129                 :                                         // dataset name
     130                 : #define L1B_NOAA9_HEADER_SIZE       122 // Terabit memory (TBM) header length
     131                 : #define L1B_NOAA9_HDR_NAME_OFF      30  // Dataset name offset
     132                 : #define L1B_NOAA9_HDR_SRC_OFF       70  // Receiving station name offset
     133                 : #define L1B_NOAA9_HDR_CHAN_OFF      97  // Selected channels map offset
     134                 : #define L1B_NOAA9_HDR_CHAN_SIZE     20  // Length of selected channels map
     135                 : #define L1B_NOAA9_HDR_WORD_OFF      117 // Sensor data word size offset
     136                 : 
     137                 : #define L1B_NOAA15_HEADER_SIZE      512 // Archive Retrieval System (ARS)
     138                 :                                         // header
     139                 : #define L1B_NOAA15_HDR_CHAN_OFF     97  // Selected channels map offset
     140                 : #define L1B_NOAA15_HDR_CHAN_SIZE    20  // Length of selected channels map
     141                 : #define L1B_NOAA15_HDR_WORD_OFF     117 // Sensor data word size offset
     142                 : 
     143                 : #define L1B_NOAA9_HDR_REC_SIZE      146 // Length of header record
     144                 :                                         // filled with the data
     145                 : #define L1B_NOAA9_HDR_REC_ID_OFF    0   // Spacecraft ID offset
     146                 : #define L1B_NOAA9_HDR_REC_PROD_OFF  1   // Data type offset
     147                 : #define L1B_NOAA9_HDR_REC_DSTAT_OFF 34  // DACS status offset
     148                 : 
     149                 : #define L1B_NOAA15_HDR_REC_SIZE     992 // Length of header record
     150                 :                                         // filled with the data
     151                 : #define L1B_NOAA15_HDR_REC_SITE_OFF 0   // Dataset creation site ID offset
     152                 : #define L1B_NOAA15_HDR_REC_NAME_OFF 22  // Dataset name
     153                 : #define L1B_NOAA15_HDR_REC_ID_OFF   72  // Spacecraft ID offset
     154                 : #define L1B_NOAA15_HDR_REC_PROD_OFF 76  // Data type offset
     155                 : #define L1B_NOAA15_HDR_REC_STAT_OFF 116 // Instrument status offset
     156                 : #define L1B_NOAA15_HDR_REC_SRC_OFF  154 // Receiving station name offset
     157                 : 
     158                 : #define DESIRED_GCPS_PER_LINE 11
     159                 : #define DESIRED_LINES_OF_GCPS 20
     160                 : 
     161                 : // Fixed values used to scale GCPs coordinates in AVHRR records
     162                 : #define L1B_NOAA9_GCP_SCALE     128.0
     163                 : #define L1B_NOAA15_GCP_SCALE    10000.0
     164                 : 
     165                 : /************************************************************************/
     166                 : /* ==================================================================== */
     167                 : /*                      TimeCode (helper class)                         */
     168                 : /* ==================================================================== */
     169                 : /************************************************************************/
     170                 : 
     171                 : #define L1B_TIMECODE_LENGTH 100
     172                 : class TimeCode {
     173                 :     long        lYear;
     174                 :     long        lDay;
     175                 :     long        lMillisecond;
     176                 :     char        pszString[L1B_TIMECODE_LENGTH];
     177                 : 
     178                 :   public:
     179              24 :     void SetYear(long year)
     180                 :     {
     181              24 :         lYear = year;
     182              24 :     }
     183              24 :     void SetDay(long day)
     184                 :     {
     185              24 :         lDay = day;
     186              24 :     }
     187              24 :     void SetMillisecond(long millisecond)
     188                 :     {
     189              24 :         lMillisecond = millisecond;
     190              24 :     }
     191              24 :     char* PrintTime()
     192                 :     {
     193                 :         snprintf(pszString, L1B_TIMECODE_LENGTH,
     194                 :                  "year: %ld, day: %ld, millisecond: %ld",
     195              24 :                  lYear, lDay, lMillisecond);
     196              24 :         return pszString;
     197                 :     }
     198                 : };
     199                 : #undef L1B_TIMECODE_LENGTH
     200                 : 
     201                 : /************************************************************************/
     202                 : /* ==================================================================== */
     203                 : /*                              L1BDataset                              */
     204                 : /* ==================================================================== */
     205                 : /************************************************************************/
     206                 : 
     207                 : class L1BDataset : public GDALPamDataset
     208                 : {
     209                 :     friend class L1BRasterBand;
     210                 : 
     211                 :     char        pszRevolution[6]; // Five-digit number identifying spacecraft revolution
     212                 :     int         eSource;        // Source of data (receiving station name)
     213                 :     int         eProcCenter;    // Data processing center
     214                 :     TimeCode    sStartTime;
     215                 :     TimeCode    sStopTime;
     216                 : 
     217                 :     GDAL_GCP    *pasGCPList;
     218                 :     int         nGCPCount;
     219                 :     int         iGCPOffset;
     220                 :     int         iGCPCodeOffset;
     221                 :     int         nGCPsPerLine;
     222                 :     int         eLocationIndicator, iGCPStart, iGCPStep;
     223                 : 
     224                 :     int         eL1BFormat;
     225                 :     int         nBufferSize;
     226                 :     int         eSpacecraftID;
     227                 :     int         eProductType;   // LAC, GAC, HRPT, FRAC
     228                 :     int         iDataFormat;    // 10-bit packed or 16-bit unpacked
     229                 :     int         nRecordDataStart;
     230                 :     int         nRecordDataEnd;
     231                 :     int         nDataStartOffset;
     232                 :     int         nRecordSize;
     233                 :     GUInt32     iInstrumentStatus;
     234                 :     GUInt32     iChannelsMask;
     235                 : 
     236                 :     char        *pszGCPProjection;
     237                 : 
     238                 :     VSILFILE   *fp;
     239                 : 
     240                 :     int         bFetchGeolocation;
     241                 :     int         bGuessDataFormat;
     242                 : 
     243                 :     void        ProcessRecordHeaders();
     244                 :     void        FetchGCPs( GDAL_GCP *, GByte *, int );
     245                 :     void        FetchNOAA9TimeCode(TimeCode *, GByte *, int *);
     246                 :     void        FetchNOAA15TimeCode(TimeCode *, GUInt16 *, int *);
     247                 :     CPLErr      ProcessDatasetHeader();
     248                 :     int         ComputeFileOffsets();
     249                 :     
     250                 :     static int  DetectFormat( GDALOpenInfo *poOpenInfo );
     251                 : 
     252                 :   public:
     253                 :                 L1BDataset( int );
     254                 :                 ~L1BDataset();
     255                 :     
     256                 :     virtual int GetGCPCount();
     257                 :     virtual const char *GetGCPProjection();
     258                 :     virtual const GDAL_GCP *GetGCPs();
     259                 : 
     260                 :     static int  Identify( GDALOpenInfo * );
     261                 :     static GDALDataset *Open( GDALOpenInfo * );
     262                 : 
     263                 : };
     264                 : 
     265                 : /************************************************************************/
     266                 : /* ==================================================================== */
     267                 : /*                            L1BRasterBand                             */
     268                 : /* ==================================================================== */
     269                 : /************************************************************************/
     270                 : 
     271                 : class L1BRasterBand : public GDALPamRasterBand
     272              53 : {
     273                 :     friend class L1BDataset;
     274                 : 
     275                 :   public:
     276                 : 
     277                 :                 L1BRasterBand( L1BDataset *, int );
     278                 :     
     279                 : //    virtual double GetNoDataValue( int *pbSuccess = NULL );
     280                 :     virtual CPLErr IReadBlock( int, int, void * );
     281                 : };
     282                 : 
     283                 : 
     284                 : /************************************************************************/
     285                 : /*                           L1BRasterBand()                            */
     286                 : /************************************************************************/
     287                 : 
     288              53 : L1BRasterBand::L1BRasterBand( L1BDataset *poDS, int nBand )
     289                 : 
     290                 : {
     291              53 :     this->poDS = poDS;
     292              53 :     this->nBand = nBand;
     293              53 :     eDataType = GDT_UInt16;
     294                 : 
     295              53 :     nBlockXSize = poDS->GetRasterXSize();
     296              53 :     nBlockYSize = 1;
     297              53 : }
     298                 : 
     299                 : /************************************************************************/
     300                 : /*                             IReadBlock()                             */
     301                 : /************************************************************************/
     302                 : 
     303             280 : CPLErr L1BRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
     304                 :                                   void * pImage )
     305                 : {
     306             280 :     L1BDataset  *poGDS = (L1BDataset *) poDS;
     307                 : 
     308                 : /* -------------------------------------------------------------------- */
     309                 : /*      Seek to data.                                                   */
     310                 : /* -------------------------------------------------------------------- */
     311                 :     int iDataOffset = (poGDS->eLocationIndicator == DESCEND) ?
     312                 :         poGDS->nDataStartOffset + nBlockYOff * poGDS->nRecordSize :
     313                 :         poGDS->nDataStartOffset +
     314             280 :             (nRasterYSize - nBlockYOff - 1) * poGDS->nRecordSize;
     315             280 :     VSIFSeekL( poGDS->fp, iDataOffset, SEEK_SET );
     316                 : 
     317                 : /* -------------------------------------------------------------------- */
     318                 : /*      Read data into the buffer.                                      */
     319                 : /* -------------------------------------------------------------------- */
     320             280 :     GUInt16     *iScan = NULL;          // Unpacked 16-bit scanline buffer
     321                 :     int         i, j;
     322                 : 
     323             280 :     switch (poGDS->iDataFormat)
     324                 :     {
     325                 :         case PACKED10BIT:
     326                 :             {
     327                 :                 // Read packed scanline
     328             102 :                 GUInt32 *iRawScan = (GUInt32 *)CPLMalloc(poGDS->nRecordSize);
     329             102 :                 VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp );
     330                 : 
     331             102 :                 iScan = (GUInt16 *)CPLMalloc(poGDS->nBufferSize);
     332             102 :                 j = 0;
     333          110646 :                 for(i = poGDS->nRecordDataStart / (int)sizeof(iRawScan[0]);
     334                 :                     i < poGDS->nRecordDataEnd / (int)sizeof(iRawScan[0]); i++)
     335                 :                 {
     336          110544 :                     GUInt32 iWord1 = CPL_MSBWORD32( iRawScan[i] );
     337          110544 :                     GUInt32 iWord2 = iWord1 & 0x3FF00000;
     338                 : 
     339          110544 :                     iScan[j++] = (GUInt16) (iWord2 >> 20);
     340          110544 :                     iWord2 = iWord1 & 0x000FFC00;
     341          110544 :                     iScan[j++] = (GUInt16) (iWord2 >> 10);
     342          110544 :                     iScan[j++] = (GUInt16) (iWord1 & 0x000003FF);
     343                 :                 }
     344             102 :                 CPLFree(iRawScan);
     345                 :             }
     346             102 :             break;
     347                 :         case UNPACKED16BIT:
     348                 :             {
     349                 :                 // Read unpacked scanline
     350              99 :                 GUInt16 *iRawScan = (GUInt16 *)CPLMalloc(poGDS->nRecordSize);
     351              99 :                 VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp );
     352                 : 
     353                 :                 iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
     354              99 :                                              * poGDS->nBands * sizeof(GUInt16));
     355          187845 :                 for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
     356                 :                 {
     357          375492 :                     iScan[i] = CPL_MSBWORD16( iRawScan[poGDS->nRecordDataStart
     358          375492 :                         / (int)sizeof(iRawScan[0]) + i] );
     359                 :                 }
     360              99 :                 CPLFree(iRawScan);
     361                 :             }
     362              99 :             break;
     363                 :         case UNPACKED8BIT:
     364                 :             {
     365                 :                 // Read 8-bit unpacked scanline
     366              79 :                 GByte   *byRawScan = (GByte *)CPLMalloc(poGDS->nRecordSize);
     367              79 :                 VSIFReadL( byRawScan, 1, poGDS->nRecordSize, poGDS->fp );
     368                 :                 
     369                 :                 iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
     370              79 :                                              * poGDS->nBands * sizeof(GUInt16));
     371          161634 :                 for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
     372          161555 :                     iScan[i] = byRawScan[poGDS->nRecordDataStart
     373          161555 :                         / (int)sizeof(byRawScan[0]) + i];
     374              79 :                 CPLFree(byRawScan);
     375                 :             }
     376                 :             break;
     377                 :         default: // NOTREACHED
     378                 :             break;
     379                 :     }
     380                 :     
     381             280 :     int nBlockSize = nBlockXSize * nBlockYSize;
     382             280 :     if (poGDS->eLocationIndicator == DESCEND)
     383                 :     {
     384          111507 :         for( i = 0, j = 0; i < nBlockSize; i++ )
     385                 :         {
     386          111287 :             ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1];
     387          111287 :             j += poGDS->nBands;
     388                 :         }
     389                 :     }
     390                 :     else
     391                 :     {
     392           36073 :         for ( i = nBlockSize - 1, j = 0; i >= 0; i-- )
     393                 :         {
     394           36013 :             ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1];
     395           36013 :             j += poGDS->nBands;
     396                 :         }
     397                 :     }
     398                 :     
     399             280 :     CPLFree(iScan);
     400             280 :     return CE_None;
     401                 : }
     402                 : 
     403                 : /************************************************************************/
     404                 : /*                           L1BDataset()                               */
     405                 : /************************************************************************/
     406                 : 
     407              12 : L1BDataset::L1BDataset( int eL1BFormat )
     408                 : 
     409                 : {
     410              12 :     this->eL1BFormat = eL1BFormat;
     411              12 :     fp = NULL;
     412              12 :     nGCPCount = 0;
     413              12 :     pasGCPList = NULL;
     414              12 :     pszGCPProjection = CPLStrdup( "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",7043]],TOWGS84[0,0,4.5,0,0,0.554,0.2263],AUTHORITY[\"EPSG\",6322]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AUTHORITY[\"EPSG\",4322]]" );
     415              12 :     nBands = 0;
     416              12 :     eLocationIndicator = DESCEND; // XXX: should be initialised
     417              12 :     iChannelsMask = 0;
     418              12 :     iInstrumentStatus = 0;
     419              12 :     bFetchGeolocation = FALSE;
     420              12 :     bGuessDataFormat = FALSE;
     421              12 : }
     422                 : 
     423                 : /************************************************************************/
     424                 : /*                            ~L1BDataset()                             */
     425                 : /************************************************************************/
     426                 : 
     427              12 : L1BDataset::~L1BDataset()
     428                 : 
     429                 : {
     430              12 :     FlushCache();
     431                 : 
     432              12 :     if( nGCPCount > 0 )
     433                 :     {
     434              12 :         GDALDeinitGCPs( nGCPCount, pasGCPList );
     435              12 :         CPLFree( pasGCPList );
     436                 :     }
     437              12 :     if ( pszGCPProjection )
     438              12 :         CPLFree( pszGCPProjection );
     439              12 :     if( fp != NULL )
     440              12 :         VSIFCloseL( fp );
     441              12 : }
     442                 : 
     443                 : /************************************************************************/
     444                 : /*                            GetGCPCount()                             */
     445                 : /************************************************************************/
     446                 : 
     447              12 : int L1BDataset::GetGCPCount()
     448                 : 
     449                 : {
     450              12 :     return nGCPCount;
     451                 : }
     452                 : 
     453                 : /************************************************************************/
     454                 : /*                          GetGCPProjection()                          */
     455                 : /************************************************************************/
     456                 : 
     457               0 : const char *L1BDataset::GetGCPProjection()
     458                 : 
     459                 : {
     460               0 :     if( nGCPCount > 0 )
     461               0 :         return pszGCPProjection;
     462                 :     else
     463               0 :         return "";
     464                 : }
     465                 : 
     466                 : /************************************************************************/
     467                 : /*                               GetGCPs()                              */
     468                 : /************************************************************************/
     469                 : 
     470              12 : const GDAL_GCP *L1BDataset::GetGCPs()
     471                 : {
     472              12 :     return pasGCPList;
     473                 : }
     474                 : 
     475                 : /************************************************************************/
     476                 : /*      Fetch timecode from the record header (NOAA9-NOAA14 version)    */
     477                 : /************************************************************************/
     478                 : 
     479              14 : void L1BDataset::FetchNOAA9TimeCode( TimeCode *psTime, GByte *piRecordHeader,
     480                 :                                      int *peLocationIndicator )
     481                 : {
     482                 :     GUInt32 lTemp;
     483                 : 
     484              14 :     lTemp = ((piRecordHeader[2] >> 1) & 0x7F);
     485                 :     psTime->SetYear((lTemp > 77) ? 
     486              14 :         (lTemp + 1900) : (lTemp + 2000)); // Avoid `Year 2000' problem
     487              14 :     psTime->SetDay((GUInt32)(piRecordHeader[2] & 0x01) << 8
     488              14 :                    | (GUInt32)piRecordHeader[3]);
     489              14 :     psTime->SetMillisecond( ((GUInt32)(piRecordHeader[4] & 0x07) << 24)
     490              14 :         | ((GUInt32)piRecordHeader[5] << 16)
     491              14 :         | ((GUInt32)piRecordHeader[6] << 8)
     492              42 :         | (GUInt32)piRecordHeader[7] );
     493              14 :     if ( peLocationIndicator )
     494                 :     {
     495                 :         *peLocationIndicator =
     496               7 :             ((piRecordHeader[8] & 0x02) == 0) ? ASCEND : DESCEND;
     497                 :     }
     498              14 : }
     499                 : 
     500                 : /************************************************************************/
     501                 : /*      Fetch timecode from the record header (NOAA15-METOP2 version)   */
     502                 : /************************************************************************/
     503                 : 
     504              10 : void L1BDataset::FetchNOAA15TimeCode( TimeCode *psTime,
     505                 :                                       GUInt16 *piRecordHeader,
     506                 :                                       int *peLocationIndicator )
     507                 : {
     508                 : #ifdef CPL_LSB
     509                 :     GUInt16 iTemp;
     510                 :     GUInt32 lTemp;
     511                 : 
     512              10 :     iTemp = piRecordHeader[1];
     513              10 :     psTime->SetYear(CPL_SWAP16(iTemp));
     514              10 :     iTemp = piRecordHeader[2];
     515              10 :     psTime->SetDay(CPL_SWAP16(iTemp));
     516              10 :     lTemp = (GUInt32)CPL_SWAP16(piRecordHeader[4]) << 16 |
     517              20 :         (GUInt32)CPL_SWAP16(piRecordHeader[5]);
     518              10 :     psTime->SetMillisecond(lTemp);
     519              10 :     if ( peLocationIndicator )
     520                 :     {
     521                 :         // FIXME: hemisphere
     522                 :         *peLocationIndicator =
     523               5 :             ((CPL_SWAP16(piRecordHeader[6]) & 0x8000) == 0) ? ASCEND : DESCEND;
     524                 :     }
     525                 : #else
     526                 :     psTime->SetYear(piRecordHeader[1]);
     527                 :     psTime->SetDay(piRecordHeader[2]);
     528                 :     psTime->SetMillisecond( (GUInt32)piRecordHeader[4] << 16
     529                 :                             | (GUInt32)piRecordHeader[5] );
     530                 :     if ( peLocationIndicator )
     531                 :     {
     532                 :         *peLocationIndicator =
     533                 :             ((piRecordHeader[6] & 0x8000) == 0) ? ASCEND : DESCEND;
     534                 :     }
     535                 : #endif
     536              10 : }
     537                 : 
     538                 : /************************************************************************/
     539                 : /*      Fetch GCPs from the individual scanlines                        */
     540                 : /************************************************************************/
     541                 : 
     542             240 : void L1BDataset::FetchGCPs( GDAL_GCP *pasGCPList,
     543                 :                             GByte *pabyRecordHeader, int iLine )
     544                 : {
     545                 :     // LAC and HRPT GCPs are tied to the center of pixel,
     546                 :     // GAC ones are slightly displaced.
     547             240 :     double  dfDelta = (eProductType == GAC) ? 0.9 : 0.5;
     548                 :     double  dfPixel = (eLocationIndicator == DESCEND) ?
     549             240 :         iGCPStart + dfDelta : (nRasterXSize - (iGCPStart + dfDelta));
     550                 : 
     551                 :     int     nGCPs;
     552             240 :     if ( eSpacecraftID <= NOAA14 )
     553                 :     {
     554                 :         // NOAA9-NOAA14 records have an indicator of number of working GCPs.
     555                 :         // Number of good GCPs may be smaller than the total amount of points.
     556                 :         nGCPs = (*(pabyRecordHeader + iGCPCodeOffset) < nGCPsPerLine) ?
     557             140 :             *(pabyRecordHeader + iGCPCodeOffset) : nGCPsPerLine;
     558                 : #ifdef DEBUG
     559                 :         CPLDebug( "L1B", "iGCPCodeOffset=%d, nGCPsPerLine=%d, nGoodGCPs=%d",
     560             140 :                   iGCPCodeOffset, nGCPsPerLine, nGCPs );
     561                 : #endif
     562                 :     }
     563                 :     else
     564             100 :         nGCPs = nGCPsPerLine;
     565                 : 
     566             240 :     pabyRecordHeader += iGCPOffset;
     567                 : 
     568           12720 :     while ( nGCPs-- )
     569                 :     {
     570           12240 :         if ( eSpacecraftID <= NOAA14 )
     571                 :         {
     572            7140 :             GInt16  nRawY = CPL_MSBWORD16( *(GInt16*)pabyRecordHeader );
     573            7140 :             pabyRecordHeader += sizeof(GInt16);
     574            7140 :             GInt16  nRawX = CPL_MSBWORD16( *(GInt16*)pabyRecordHeader );
     575            7140 :             pabyRecordHeader += sizeof(GInt16);
     576                 : 
     577            7140 :             pasGCPList[nGCPCount].dfGCPY = nRawY / L1B_NOAA9_GCP_SCALE;
     578            7140 :             pasGCPList[nGCPCount].dfGCPX = nRawX / L1B_NOAA9_GCP_SCALE;
     579                 :         }
     580                 :         else
     581                 :         {
     582            5100 :             GInt32  nRawY = CPL_MSBWORD32( *(GInt32*)pabyRecordHeader );
     583            5100 :             pabyRecordHeader += sizeof(GInt32);
     584            5100 :             GInt32  nRawX = CPL_MSBWORD32( *(GInt32*)pabyRecordHeader );
     585            5100 :             pabyRecordHeader += sizeof(GInt32);
     586                 : 
     587            5100 :             pasGCPList[nGCPCount].dfGCPY = nRawY / L1B_NOAA15_GCP_SCALE;
     588            5100 :             pasGCPList[nGCPCount].dfGCPX = nRawX / L1B_NOAA15_GCP_SCALE;
     589                 :         }
     590                 : 
     591           48960 :         if ( pasGCPList[nGCPCount].dfGCPX < -180
     592           12240 :              || pasGCPList[nGCPCount].dfGCPX > 180
     593           12240 :              || pasGCPList[nGCPCount].dfGCPY < -90
     594           12240 :              || pasGCPList[nGCPCount].dfGCPY > 90 )
     595               0 :             continue;
     596                 : 
     597           12240 :         pasGCPList[nGCPCount].dfGCPZ = 0.0;
     598           12240 :         pasGCPList[nGCPCount].dfGCPPixel = dfPixel;
     599           12240 :         dfPixel += (eLocationIndicator == DESCEND) ? iGCPStep : -iGCPStep;
     600           12240 :         pasGCPList[nGCPCount].dfGCPLine =
     601                 :             (double)( (eLocationIndicator == DESCEND) ?
     602           12240 :                 iLine : nRasterYSize - iLine - 1 ) + 0.5;
     603           12240 :         nGCPCount++;
     604                 :     }
     605             240 : }
     606                 : 
     607                 : /************************************************************************/
     608                 : /*                      ProcessRecordHeaders()                          */
     609                 : /************************************************************************/
     610                 : 
     611              12 : void L1BDataset::ProcessRecordHeaders()
     612                 : {
     613              12 :     void    *pRecordHeader = CPLMalloc( nRecordDataStart );
     614                 : 
     615              12 :     VSIFSeekL(fp, nDataStartOffset, SEEK_SET);
     616              12 :     VSIFReadL(pRecordHeader, 1, nRecordDataStart, fp);
     617                 : 
     618              12 :     if (eSpacecraftID <= NOAA14)
     619                 :     {
     620                 :         FetchNOAA9TimeCode( &sStartTime, (GByte *) pRecordHeader,
     621               7 :                             &eLocationIndicator );
     622                 :     }
     623                 :     else
     624                 :     {
     625                 :         FetchNOAA15TimeCode( &sStartTime, (GUInt16 *) pRecordHeader,
     626               5 :                              &eLocationIndicator );
     627                 :     }
     628                 : 
     629                 :     VSIFSeekL( fp, nDataStartOffset + (nRasterYSize - 1) * nRecordSize,
     630              12 :               SEEK_SET);
     631              12 :     VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp );
     632                 : 
     633              12 :     if (eSpacecraftID <= NOAA14)
     634               7 :         FetchNOAA9TimeCode( &sStopTime, (GByte *) pRecordHeader, NULL );
     635                 :     else
     636               5 :         FetchNOAA15TimeCode( &sStopTime, (GUInt16 *) pRecordHeader, NULL );
     637                 : 
     638                 : /* -------------------------------------------------------------------- */
     639                 : /*      Pick a skip factor so that we will get roughly 20 lines         */
     640                 : /*      worth of GCPs.  That should give respectible coverage on all    */
     641                 : /*      but the longest swaths.                                         */
     642                 : /* -------------------------------------------------------------------- */
     643              12 :     int nTargetLines = DESIRED_LINES_OF_GCPS;
     644              12 :     int nLineSkip = nRasterYSize / ( nTargetLines - 1 );
     645                 :     
     646                 : /* -------------------------------------------------------------------- */
     647                 : /*      Initialize the GCP list.                                        */
     648                 : /* -------------------------------------------------------------------- */
     649                 :     pasGCPList = (GDAL_GCP *)VSICalloc( nTargetLines * nGCPsPerLine,
     650              12 :                                         sizeof(GDAL_GCP) );
     651              12 :     if (pasGCPList == NULL)
     652                 :     {
     653               0 :         CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
     654               0 :         CPLFree( pRecordHeader );
     655               0 :         return;
     656                 :     }
     657              12 :     GDALInitGCPs( nTargetLines * nGCPsPerLine, pasGCPList );
     658                 : 
     659                 : /* -------------------------------------------------------------------- */
     660                 : /*      Fetch the GCPs for each selected line.  We force the last       */
     661                 : /*      line sampled to be the last line in the dataset even if that    */
     662                 : /*      leaves a bigger than expected gap.                              */
     663                 : /* -------------------------------------------------------------------- */
     664                 :     int iStep;
     665                 : 
     666             252 :     for( iStep = 0; iStep < nTargetLines; iStep++ )
     667                 :     {
     668             240 :         int nOrigGCPs = nGCPCount;
     669                 :         int iLine;
     670                 : 
     671             240 :         if( iStep == nTargetLines - 1 )
     672              12 :             iLine = nRasterXSize - 1;
     673                 :         else
     674             228 :             iLine = nLineSkip * iStep;
     675                 : 
     676             240 :         VSIFSeekL( fp, nDataStartOffset + iLine * nRecordSize, SEEK_SET );
     677             240 :         VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp );
     678                 : 
     679             240 :         FetchGCPs( pasGCPList, (GByte *)pRecordHeader, iLine );
     680                 : 
     681                 : /* -------------------------------------------------------------------- */
     682                 : /*      We don't really want too many GCPs per line.  Downsample to     */
     683                 : /*      11 per line.                                                    */
     684                 : /* -------------------------------------------------------------------- */
     685                 :         int iGCP;
     686             240 :         int nGCPsOnThisLine = nGCPCount - nOrigGCPs;
     687             240 :         int nDesiredGCPsPerLine = MIN(DESIRED_GCPS_PER_LINE,nGCPsOnThisLine);
     688                 :         int nGCPStep = ( nDesiredGCPsPerLine > 1 ) ?
     689             240 :             ( nGCPsOnThisLine - 1 ) / ( nDesiredGCPsPerLine-1 ) : 1;
     690             240 :         int iSrcGCP = nOrigGCPs;
     691             240 :         int iDstGCP = nOrigGCPs;
     692                 : 
     693             240 :         if( nGCPStep == 0 )
     694               0 :             nGCPStep = 1;
     695                 : 
     696            2880 :         for( iGCP = 0; iGCP < nDesiredGCPsPerLine; iGCP++ )
     697                 :         {
     698            2640 :             iSrcGCP += iGCP * nGCPStep;
     699            2640 :             iDstGCP += iGCP;
     700                 : 
     701            2640 :             pasGCPList[iDstGCP].dfGCPX = pasGCPList[iSrcGCP].dfGCPX;
     702            2640 :             pasGCPList[iDstGCP].dfGCPY = pasGCPList[iSrcGCP].dfGCPY;
     703            2640 :             pasGCPList[iDstGCP].dfGCPPixel = pasGCPList[iSrcGCP].dfGCPPixel;
     704            2640 :             pasGCPList[iDstGCP].dfGCPLine = pasGCPList[iSrcGCP].dfGCPLine;
     705                 :         }
     706                 : 
     707             240 :         nGCPCount = nOrigGCPs + nDesiredGCPsPerLine;
     708                 :     }
     709                 : 
     710              12 :     if( nGCPCount < nTargetLines * nGCPsPerLine )
     711                 :     {
     712                 :         GDALDeinitGCPs( nTargetLines * nGCPsPerLine - nGCPCount, 
     713              12 :                         pasGCPList + nGCPCount );
     714                 :     }
     715                 : 
     716              12 :     CPLFree( pRecordHeader );
     717                 : 
     718                 : /* -------------------------------------------------------------------- */
     719                 : /*      Set fetched information as metadata records                     */
     720                 : /* -------------------------------------------------------------------- */
     721                 :     // Time of first scanline
     722              12 :     SetMetadataItem( "START",  sStartTime.PrintTime() );
     723                 :     // Time of last scanline
     724              12 :     SetMetadataItem( "STOP",  sStopTime.PrintTime() );
     725                 :     // AVHRR Earth location indication
     726                 : 
     727              12 :     switch( eLocationIndicator )
     728                 :     {
     729                 :         case ASCEND:
     730               4 :             SetMetadataItem( "LOCATION", "Ascending" );
     731               4 :             break;
     732                 :         case DESCEND:
     733                 :         default:
     734               8 :             SetMetadataItem( "LOCATION", "Descending" );
     735                 :             break;
     736                 :     }
     737                 : 
     738                 : }
     739                 : 
     740                 : /************************************************************************/
     741                 : /*                      ProcessDatasetHeader()                          */
     742                 : /************************************************************************/
     743                 : 
     744              12 : CPLErr L1BDataset::ProcessDatasetHeader()
     745                 : {
     746                 :     char    szDatasetName[L1B_DATASET_NAME_SIZE + 1];
     747                 : 
     748              12 :     if ( eL1BFormat == L1B_NOAA9 )
     749                 :     {
     750                 :         GByte   abyTBMHeader[L1B_NOAA9_HEADER_SIZE];
     751                 : 
     752               7 :         if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0
     753                 :              || VSIFReadL( abyTBMHeader, 1, L1B_NOAA9_HEADER_SIZE,
     754                 :                            fp ) < L1B_NOAA9_HEADER_SIZE )
     755                 :         {
     756               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 TBM header." );
     757               0 :             return CE_Failure;
     758                 :         }
     759                 : 
     760                 :         // Fetch dataset name. NOAA-9/14 datasets contain the names in TBM
     761                 :         // header only, so read it there.
     762                 :         memcpy( szDatasetName, abyTBMHeader + L1B_NOAA9_HDR_NAME_OFF,
     763               7 :                 L1B_DATASET_NAME_SIZE );
     764               7 :         szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
     765                 : 
     766                 :         // Determine processing center where the dataset was created
     767               7 :         if ( EQUALN((const char *)abyTBMHeader
     768                 :                     + L1B_NOAA9_HDR_NAME_OFF, "CMS", 3) )
     769               0 :              eProcCenter = CMS;
     770               7 :         else if ( EQUALN((const char *)abyTBMHeader
     771                 :                          + L1B_NOAA9_HDR_NAME_OFF, "DSS", 3) )
     772               0 :              eProcCenter = DSS;
     773               7 :         else if ( EQUALN((const char *)abyTBMHeader
     774                 :                          + L1B_NOAA9_HDR_NAME_OFF, "NSS", 3) )
     775               7 :              eProcCenter = NSS;
     776               0 :         else if ( EQUALN((const char *)abyTBMHeader
     777                 :                          + L1B_NOAA9_HDR_NAME_OFF, "UKM", 3) )
     778               0 :              eProcCenter = UKM;
     779                 :         else
     780               0 :              eProcCenter = UNKNOWN_CENTER;
     781                 : 
     782                 :         // Determine number of bands
     783                 :         int     i;
     784             147 :         for ( i = 0; i < L1B_NOAA9_HDR_CHAN_SIZE; i++ )
     785                 :         {
     786             267 :             if ( abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 1
     787             127 :                  || abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 'Y' )
     788                 :             {
     789              13 :                 nBands++;
     790              13 :                 iChannelsMask |= (1 << i);
     791                 :             }
     792                 :         }
     793               7 :         if ( nBands == 0 || nBands > 5 )
     794                 :         {
     795               3 :             nBands = 5;
     796               3 :             iChannelsMask = 0x1F;
     797                 :         }
     798                 : 
     799                 :         // Determine data format (10-bit packed or 8/16-bit unpacked)
     800               7 :         if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     801                 :                     "10", 2) )
     802               1 :             iDataFormat = PACKED10BIT;
     803               6 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     804                 :                          "16", 2) )
     805               1 :             iDataFormat = UNPACKED16BIT;
     806               5 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     807                 :                          "08", 2) )
     808               1 :             iDataFormat = UNPACKED8BIT;
     809              12 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     810                 :                          "  ", 2)
     811               4 :                   || abyTBMHeader[L1B_NOAA9_HDR_WORD_OFF] == '\0' )
     812                 :             /* Empty string can be found in the following samples : 
     813                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/franh.1b (10 bit)
     814                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/frang.1b (10 bit)
     815                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/calfilel.1b (16 bit)
     816                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/rapnzg.1b (16 bit)
     817                 :                 ftp://ftp.sat.dundee.ac.uk/misc/testdata/noaa12/hrptnoaa1b.dat (10 bit)
     818                 :             */
     819               4 :             bGuessDataFormat = TRUE;
     820                 :         else
     821                 :         {
     822                 : #ifdef DEBUG
     823                 :             CPLDebug( "L1B", "Unknown data format \"%.2s\".",
     824               0 :                       abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF );
     825                 : #endif
     826               0 :             return CE_Failure;
     827                 :         }
     828                 : 
     829                 :         // Now read the dataset header record
     830                 :         GByte   abyRecHeader[L1B_NOAA9_HDR_REC_SIZE];
     831               7 :         if ( VSIFSeekL( fp, L1B_NOAA9_HEADER_SIZE, SEEK_SET ) < 0
     832                 :              || VSIFReadL( abyRecHeader, 1, L1B_NOAA9_HDR_REC_SIZE,
     833                 :                            fp ) < L1B_NOAA9_HDR_REC_SIZE )
     834                 :         {
     835               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 record header." );
     836               0 :             return CE_Failure;
     837                 :         }
     838                 : 
     839                 :         // Determine the spacecraft name
     840               7 :         switch ( abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] )
     841                 :         {
     842                 :             /* FIXME: use time code to determine TIROS-N, because the SatID
     843                 :              * identical to NOAA-11
     844                 :              * case 1:
     845                 :                 eSpacecraftID = TIROSN;
     846                 :                 break;
     847                 :             case 2:
     848                 :                 eSpacecraftID = NOAA6;
     849                 :                 break;*/
     850                 :             case 4:
     851               0 :                 eSpacecraftID = NOAA7;
     852               0 :                 break;
     853                 :             case 6:
     854               0 :                 eSpacecraftID = NOAA8;
     855               0 :                 break;
     856                 :             case 7:
     857               0 :                 eSpacecraftID = NOAA9;
     858               0 :                 break;
     859                 :             case 8:
     860               0 :                 eSpacecraftID = NOAA10;
     861               0 :                 break;
     862                 :             case 1:
     863               0 :                 eSpacecraftID = NOAA11;
     864               0 :                 break;
     865                 :             case 5:
     866               4 :                 eSpacecraftID = NOAA12;
     867               4 :                 break;
     868                 :             case 2:
     869               0 :                 eSpacecraftID = NOAA13;
     870               0 :                 break;
     871                 :             case 3:
     872               3 :                 eSpacecraftID = NOAA14;
     873               3 :                 break;
     874                 :             default:
     875                 : #ifdef DEBUG
     876                 :                 CPLDebug( "L1B", "Unknown spacecraft ID \"%d\".",
     877               0 :                           abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] );
     878                 : #endif
     879               0 :                 return CE_Failure;
     880                 :         }
     881                 : 
     882                 :         // Determine the product data type
     883               7 :         int iWord = abyRecHeader[L1B_NOAA9_HDR_REC_PROD_OFF] >> 4;
     884               7 :         switch ( iWord )
     885                 :         {
     886                 :             case 1:
     887               1 :                 eProductType = LAC;
     888               1 :                 break;
     889                 :             case 2:
     890               5 :                 eProductType = GAC;
     891               5 :                 break;
     892                 :             case 3:
     893               1 :                 eProductType = HRPT;
     894               1 :                 break;
     895                 :             default:
     896                 : #ifdef DEBUG
     897               0 :                 CPLDebug( "L1B", "Unknown product type \"%d\".", iWord );
     898                 : #endif
     899               0 :                 return CE_Failure;
     900                 :         }
     901                 : 
     902                 :         // Determine receiving station name
     903               7 :         iWord = ( abyRecHeader[L1B_NOAA9_HDR_REC_DSTAT_OFF] & 0x60 ) >> 5;
     904               7 :         switch( iWord )
     905                 :         {
     906                 :             case 1:
     907               1 :                 eSource = GC;
     908               1 :                 break;
     909                 :             case 2:
     910               6 :                 eSource = WI;
     911               6 :                 break;
     912                 :             case 3:
     913               0 :                 eSource = SO;
     914               0 :                 break;
     915                 :             default:
     916               0 :                 eSource = UNKNOWN_STATION;
     917                 :                 break;
     918                 :         }
     919                 :     }
     920                 : 
     921              10 :     else if ( eL1BFormat == L1B_NOAA15 || eL1BFormat == L1B_NOAA15_NOHDR )
     922                 :     {
     923               5 :         if ( eL1BFormat == L1B_NOAA15 )
     924                 :         {
     925                 :             GByte   abyARSHeader[L1B_NOAA15_HEADER_SIZE];
     926                 : 
     927               5 :             if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0
     928                 :                  || VSIFReadL( abyARSHeader, 1, L1B_NOAA15_HEADER_SIZE,
     929                 :                                fp ) < L1B_NOAA15_HEADER_SIZE )
     930                 :             {
     931               0 :                 CPLDebug( "L1B", "Can't read NOAA-15 ARS header." );
     932               0 :                 return CE_Failure;
     933                 :             }
     934                 : 
     935                 :             // Determine number of bands
     936                 :             int     i;
     937             105 :             for ( i = 0; i < L1B_NOAA15_HDR_CHAN_SIZE; i++ )
     938                 :             {
     939             200 :                 if ( abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 1
     940             100 :                      || abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 'Y' )
     941                 :                 {
     942              70 :                     nBands++;
     943              70 :                     iChannelsMask |= (1 << i);
     944                 :                 }
     945                 :             }
     946               5 :             if ( nBands == 0 || nBands > 5 )
     947                 :             {
     948               3 :                 nBands = 5;
     949               3 :                 iChannelsMask = 0x1F;
     950                 :             }
     951                 : 
     952                 :             // Determine data format (10-bit packed or 8/16-bit unpacked)
     953               5 :             if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     954                 :                         "10", 2) )
     955               3 :                 iDataFormat = PACKED10BIT;
     956               2 :             else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     957                 :                              "16", 2) )
     958               1 :                 iDataFormat = UNPACKED16BIT;
     959               1 :             else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     960                 :                              "08", 2) )
     961               1 :                 iDataFormat = UNPACKED8BIT;
     962                 :             else
     963                 :             {
     964                 : #ifdef DEBUG
     965                 :                 CPLDebug( "L1B", "Unknown data format \"%.2s\".",
     966               0 :                           abyARSHeader + L1B_NOAA9_HDR_WORD_OFF );
     967                 : #endif
     968               0 :                 return CE_Failure;
     969                 :             }
     970                 :         }
     971                 :         else
     972                 :         {
     973               0 :             nBands = 5;
     974               0 :             iChannelsMask = 0x1F;
     975               0 :             iDataFormat = PACKED10BIT;
     976                 :         }
     977                 : 
     978                 :         // Now read the dataset header record
     979                 :         GByte   abyRecHeader[L1B_NOAA15_HDR_REC_SIZE];
     980               5 :         if ( VSIFSeekL( fp,
     981                 :                         (eL1BFormat == L1B_NOAA15) ? L1B_NOAA15_HEADER_SIZE : 0,
     982                 :                         SEEK_SET ) < 0
     983                 :              || VSIFReadL( abyRecHeader, 1, L1B_NOAA15_HDR_REC_SIZE,
     984                 :                            fp ) < L1B_NOAA15_HDR_REC_SIZE )
     985                 :         {
     986               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 record header." );
     987               0 :             return CE_Failure;
     988                 :         }
     989                 : 
     990                 :         // Fetch dataset name
     991                 :         memcpy( szDatasetName, abyRecHeader + L1B_NOAA15_HDR_REC_NAME_OFF,
     992               5 :                 L1B_DATASET_NAME_SIZE );
     993               5 :         szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
     994                 : 
     995                 :         // Determine processing center where the dataset was created
     996               5 :         if ( EQUALN((const char *)abyRecHeader
     997                 :                     + L1B_NOAA15_HDR_REC_SITE_OFF, "CMS", 3) )
     998               0 :              eProcCenter = CMS;
     999               5 :         else if ( EQUALN((const char *)abyRecHeader
    1000                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "DSS", 3) )
    1001               2 :              eProcCenter = DSS;
    1002               3 :         else if ( EQUALN((const char *)abyRecHeader
    1003                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "NSS", 3) )
    1004               3 :              eProcCenter = NSS;
    1005               0 :         else if ( EQUALN((const char *)abyRecHeader
    1006                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "UKM", 3) )
    1007               0 :              eProcCenter = UKM;
    1008                 :         else
    1009               0 :              eProcCenter = UNKNOWN_CENTER;
    1010                 : 
    1011                 :         // Determine the spacecraft name
    1012               5 :         int iWord = CPL_MSBWORD16( *(GUInt16 *)
    1013                 :             (abyRecHeader + L1B_NOAA15_HDR_REC_ID_OFF) );
    1014               5 :         switch ( iWord )
    1015                 :         {
    1016                 :             case 2:
    1017               1 :                 eSpacecraftID = NOAA16;
    1018               1 :                 break;
    1019                 :             case 4:
    1020               1 :                 eSpacecraftID = NOAA15;
    1021               1 :                 break;
    1022                 :             case 6:
    1023               1 :                 eSpacecraftID = NOAA17;
    1024               1 :                 break;
    1025                 :             case 7:
    1026               1 :                 eSpacecraftID = NOAA18;
    1027               1 :                 break;
    1028                 :             /* FIXME: find appropriate samples and test these two cases:
    1029                 :              * case 8:
    1030                 :                 eSpacecraftID = NOAA-N';
    1031                 :                 break;
    1032                 :             case 11:
    1033                 :                 eSpacecraftID = METOP-1;
    1034                 :                 break;*/
    1035                 :             case 12:
    1036                 :             case 14:    // METOP simulator (code used in AAPP format)
    1037               1 :                 eSpacecraftID = METOP2;
    1038               1 :                 break;
    1039                 :             default:
    1040                 : #ifdef DEBUG
    1041               0 :                 CPLDebug( "L1B", "Unknown spacecraft ID \"%d\".", iWord );
    1042                 : #endif
    1043               0 :                 return CE_Failure;
    1044                 :         }
    1045                 : 
    1046                 :         // Determine the product data type
    1047                 :         iWord = CPL_MSBWORD16( *(GUInt16 *)
    1048               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_PROD_OFF) );
    1049               5 :         switch ( iWord )
    1050                 :         {
    1051                 :             case 1:
    1052               0 :                 eProductType = LAC;
    1053               0 :                 break;
    1054                 :             case 2:
    1055               3 :                 eProductType = GAC;
    1056               3 :                 break;
    1057                 :             case 3:
    1058               1 :                 eProductType = HRPT;
    1059               1 :                 break;
    1060                 :             case 4:     // XXX: documentation specifies the code '4'
    1061                 :             case 13:    // for FRAC but real datasets contain '13 here.'
    1062               1 :                 eProductType = FRAC;
    1063               1 :                 break;
    1064                 :             default:
    1065                 : #ifdef DEBUG
    1066               0 :                 CPLDebug( "L1B", "Unknown product type \"%d\".", iWord );
    1067                 : #endif
    1068               0 :                 return CE_Failure;
    1069                 :         }
    1070                 : 
    1071                 :         // Fetch hinstrument status. Helps to determine whether we have
    1072                 :         // 3A or 3B channel in the dataset.
    1073                 :         iInstrumentStatus = CPL_MSBWORD32( *(GUInt32 *)
    1074               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_STAT_OFF) );
    1075                 : 
    1076                 :         // Determine receiving station name
    1077                 :         iWord = CPL_MSBWORD16( *(GUInt16 *)
    1078               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_SRC_OFF) );
    1079               5 :         switch( iWord )
    1080                 :         {
    1081                 :             case 1:
    1082               2 :                 eSource = GC;
    1083               2 :                 break;
    1084                 :             case 2:
    1085               1 :                 eSource = WI;
    1086               1 :                 break;
    1087                 :             case 3:
    1088               0 :                 eSource = SO;
    1089               0 :                 break;
    1090                 :             case 4:
    1091               0 :                 eSource = SV;
    1092               0 :                 break;
    1093                 :             case 5:
    1094               0 :                 eSource = MO;
    1095               0 :                 break;
    1096                 :             default:
    1097               2 :                 eSource = UNKNOWN_STATION;
    1098                 :                 break;
    1099                 :         }
    1100                 :     }
    1101                 :     else
    1102               0 :         return CE_Failure;
    1103                 : 
    1104                 : /* -------------------------------------------------------------------- */
    1105                 : /*      Set fetched information as metadata records                     */
    1106                 : /* -------------------------------------------------------------------- */
    1107                 :     const char *pszText;
    1108                 : 
    1109              12 :     SetMetadataItem( "DATASET_NAME",  szDatasetName );
    1110                 : 
    1111              12 :     switch( eSpacecraftID )
    1112                 :     {
    1113                 :         case TIROSN:
    1114               0 :             pszText = "TIROS-N";
    1115               0 :             break;
    1116                 :         case NOAA6:
    1117               0 :             pszText = "NOAA-6(A)";
    1118               0 :             break;
    1119                 :         case NOAAB:
    1120               0 :             pszText = "NOAA-B";
    1121               0 :             break;
    1122                 :         case NOAA7:
    1123               0 :             pszText = "NOAA-7(C)";
    1124               0 :             break;
    1125                 :         case NOAA8:
    1126               0 :             pszText = "NOAA-8(E)";
    1127               0 :             break;
    1128                 :         case NOAA9:
    1129               0 :             pszText = "NOAA-9(F)";
    1130               0 :             break;
    1131                 :         case NOAA10:
    1132               0 :             pszText = "NOAA-10(G)";
    1133               0 :             break;
    1134                 :         case NOAA11:
    1135               0 :             pszText = "NOAA-11(H)";
    1136               0 :             break;
    1137                 :         case NOAA12:
    1138               4 :             pszText = "NOAA-12(D)";
    1139               4 :             break;
    1140                 :         case NOAA13:
    1141               0 :             pszText = "NOAA-13(I)";
    1142               0 :             break;
    1143                 :         case NOAA14:
    1144               3 :             pszText = "NOAA-14(J)";
    1145               3 :             break;
    1146                 :         case NOAA15:
    1147               1 :             pszText = "NOAA-15(K)";
    1148               1 :             break;
    1149                 :         case NOAA16:
    1150               1 :             pszText = "NOAA-16(L)";
    1151               1 :             break;
    1152                 :         case NOAA17:
    1153               1 :             pszText = "NOAA-17(M)";
    1154               1 :             break;
    1155                 :         case NOAA18:
    1156               1 :             pszText = "NOAA-18(N)";
    1157               1 :             break;
    1158                 :         case METOP2:
    1159               1 :             pszText = "METOP-2(A)";
    1160               1 :             break;
    1161                 :         default:
    1162               0 :             pszText = "Unknown";
    1163                 :             break;
    1164                 :     }
    1165              12 :     SetMetadataItem( "SATELLITE",  pszText );
    1166                 : 
    1167              12 :     switch( eProductType )
    1168                 :     {
    1169                 :         case LAC:
    1170               1 :             pszText = "AVHRR LAC";
    1171               1 :             break;
    1172                 :         case HRPT:
    1173               2 :             pszText = "AVHRR HRPT";
    1174               2 :             break;
    1175                 :         case GAC:
    1176               8 :             pszText = "AVHRR GAC";
    1177               8 :             break;
    1178                 :         case FRAC:
    1179               1 :             pszText = "AVHRR FRAC";
    1180               1 :             break;
    1181                 :         default:
    1182               0 :             pszText = "Unknown";
    1183                 :             break;
    1184                 :     }
    1185              12 :     SetMetadataItem( "DATA_TYPE",  pszText );
    1186                 : 
    1187                 :     // Get revolution number as string, we don't need this value for processing
    1188                 :     char    szRevolution[6];
    1189              12 :     memcpy( szRevolution, szDatasetName + 32, 5 );
    1190              12 :     szRevolution[5] = '\0';
    1191              12 :     SetMetadataItem( "REVOLUTION",  szRevolution );
    1192                 : 
    1193              12 :     switch( eSource )
    1194                 :     {
    1195                 :         case DU:
    1196               0 :             pszText = "Dundee, Scotland, UK";
    1197               0 :             break;
    1198                 :         case GC:
    1199               3 :             pszText = "Fairbanks, Alaska, USA (formerly Gilmore Creek)";
    1200               3 :             break;
    1201                 :         case HO:
    1202               0 :             pszText = "Honolulu, Hawaii, USA";
    1203               0 :             break;
    1204                 :         case MO:
    1205               0 :             pszText = "Monterey, California, USA";
    1206               0 :             break;
    1207                 :         case WE:
    1208               0 :             pszText = "Western Europe CDA, Lannion, France";
    1209               0 :             break;
    1210                 :         case SO:
    1211               0 :             pszText = "SOCC (Satellite Operations Control Center), Suitland, Maryland, USA";
    1212               0 :             break;
    1213                 :         case WI:
    1214               7 :             pszText = "Wallops Island, Virginia, USA";
    1215               7 :             break;
    1216                 :         default:
    1217               2 :             pszText = "Unknown receiving station";
    1218                 :             break;
    1219                 :     }
    1220              12 :     SetMetadataItem( "SOURCE",  pszText );
    1221                 : 
    1222              12 :     switch( eProcCenter )
    1223                 :     {
    1224                 :         case CMS:
    1225               0 :             pszText = "Centre de Meteorologie Spatiale - Lannion, France";
    1226               0 :             break;
    1227                 :         case DSS:
    1228               2 :             pszText = "Dundee Satellite Receiving Station - Dundee, Scotland, UK";
    1229               2 :             break;
    1230                 :         case NSS:
    1231              10 :             pszText = "NOAA/NESDIS - Suitland, Maryland, USA";
    1232              10 :             break;
    1233                 :         case UKM:
    1234               0 :             pszText = "United Kingdom Meteorological Office - Bracknell, England, UK";
    1235               0 :             break;
    1236                 :         default:
    1237               0 :             pszText = "Unknown processing center";
    1238                 :             break;
    1239                 :     }
    1240              12 :     SetMetadataItem( "PROCESSING_CENTER",  pszText );
    1241                 :     
    1242              12 :     return CE_None;
    1243                 : }
    1244                 : 
    1245                 : /************************************************************************/
    1246                 : /*                        ComputeFileOffsets()                          */
    1247                 : /************************************************************************/
    1248                 : 
    1249              16 : int L1BDataset::ComputeFileOffsets()
    1250                 : {
    1251              16 :     switch( eProductType )
    1252                 :     {
    1253                 :         case HRPT:
    1254                 :         case LAC:
    1255                 :         case FRAC:
    1256               6 :             nRasterXSize = 2048;
    1257               6 :             nBufferSize = 20484;
    1258               6 :             iGCPStart = 25;
    1259               6 :             iGCPStep = 40;
    1260               6 :             nGCPsPerLine = 51;
    1261               6 :             if ( eL1BFormat == L1B_NOAA9 )
    1262                 :             {
    1263               4 :                 if (iDataFormat == PACKED10BIT)
    1264                 :                 {
    1265               2 :                     nRecordSize = 14800;
    1266               2 :                     nRecordDataEnd = 14104;
    1267                 :                 }
    1268               2 :                 else if (iDataFormat == UNPACKED16BIT)
    1269                 :                 {
    1270               1 :                     switch(nBands)
    1271                 :                     {
    1272                 :                         case 1:
    1273               1 :                         nRecordSize = 4544;
    1274               1 :                         nRecordDataEnd = 4544;
    1275               1 :                         break;
    1276                 :                         case 2:
    1277               0 :                         nRecordSize = 8640;
    1278               0 :                         nRecordDataEnd = 8640;
    1279               0 :                         break;
    1280                 :                         case 3:
    1281               0 :                         nRecordSize = 12736;
    1282               0 :                         nRecordDataEnd = 12736;
    1283               0 :                         break;
    1284                 :                         case 4:
    1285               0 :                         nRecordSize = 16832;
    1286               0 :                         nRecordDataEnd = 16832;
    1287               0 :                         break;
    1288                 :                         case 5:
    1289               0 :                         nRecordSize = 20928;
    1290               0 :                         nRecordDataEnd = 20928;
    1291                 :                         break;
    1292                 :                     }
    1293                 :                 }
    1294                 :                 else // UNPACKED8BIT
    1295                 :                 {
    1296               1 :                     switch(nBands)
    1297                 :                     {
    1298                 :                         case 1:
    1299               1 :                         nRecordSize = 2496;
    1300               1 :                         nRecordDataEnd = 2496;
    1301               1 :                         break;
    1302                 :                         case 2:
    1303               0 :                         nRecordSize = 4544;
    1304               0 :                         nRecordDataEnd = 4544;
    1305               0 :                         break;
    1306                 :                         case 3:
    1307               0 :                         nRecordSize = 6592;
    1308               0 :                         nRecordDataEnd = 6592;
    1309               0 :                         break;
    1310                 :                         case 4:
    1311               0 :                         nRecordSize = 8640;
    1312               0 :                         nRecordDataEnd = 8640;
    1313               0 :                         break;
    1314                 :                         case 5:
    1315               0 :                         nRecordSize = 10688;
    1316               0 :                         nRecordDataEnd = 10688;
    1317                 :                         break;
    1318                 :                     }
    1319                 :                 }
    1320               4 :                 nDataStartOffset = nRecordSize + L1B_NOAA9_HEADER_SIZE;
    1321               4 :                 nRecordDataStart = 448;
    1322               4 :                 iGCPCodeOffset = 52;
    1323               4 :                 iGCPOffset = 104;
    1324                 :             }
    1325                 : 
    1326               4 :             else if ( eL1BFormat == L1B_NOAA15
    1327                 :                       || eL1BFormat == L1B_NOAA15_NOHDR )
    1328                 :             {
    1329               2 :                 if (iDataFormat == PACKED10BIT)
    1330                 :                 {
    1331               2 :                     nRecordSize = 15872;
    1332               2 :                     nRecordDataEnd = 14920;
    1333                 :                 }
    1334               0 :                 else if (iDataFormat == UNPACKED16BIT)
    1335                 :                 {
    1336               0 :                     switch(nBands)
    1337                 :                     {
    1338                 :                         case 1:
    1339               0 :                         nRecordSize = 6144;
    1340               0 :                         nRecordDataEnd = 5360;
    1341               0 :                         break;
    1342                 :                         case 2:
    1343               0 :                         nRecordSize = 10240;
    1344               0 :                         nRecordDataEnd = 9456;
    1345               0 :                         break;
    1346                 :                         case 3:
    1347               0 :                         nRecordSize = 14336;
    1348               0 :                         nRecordDataEnd = 13552;
    1349               0 :                         break;
    1350                 :                         case 4:
    1351               0 :                         nRecordSize = 18432;
    1352               0 :                         nRecordDataEnd = 17648;
    1353               0 :                         break;
    1354                 :                         case 5:
    1355               0 :                         nRecordSize = 22528;
    1356               0 :                         nRecordDataEnd = 21744;
    1357                 :                         break;
    1358                 :                     }
    1359                 :                 }
    1360                 :                 else // UNPACKED8BIT
    1361                 :                 {
    1362               0 :                     switch(nBands)
    1363                 :                     {
    1364                 :                         case 1:
    1365               0 :                         nRecordSize = 4096;
    1366               0 :                         nRecordDataEnd = 3312;
    1367               0 :                         break;
    1368                 :                         case 2:
    1369               0 :                         nRecordSize = 6144;
    1370               0 :                         nRecordDataEnd = 5360;
    1371               0 :                         break;
    1372                 :                         case 3:
    1373               0 :                         nRecordSize = 8192;
    1374               0 :                         nRecordDataEnd = 7408;
    1375               0 :                         break;
    1376                 :                         case 4:
    1377               0 :                         nRecordSize = 10240;
    1378               0 :                         nRecordDataEnd = 9456;
    1379               0 :                         break;
    1380                 :                         case 5:
    1381               0 :                         nRecordSize = 12288;
    1382               0 :                         nRecordDataEnd = 11504;
    1383                 :                         break;
    1384                 :                     }
    1385                 :                 }
    1386                 :                 nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ?
    1387               2 :                     nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
    1388               2 :                 nRecordDataStart = 1264;
    1389               2 :                 iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
    1390               2 :                 iGCPOffset = 640;
    1391                 :             }
    1392                 :             else
    1393               0 :                 return 0;
    1394               6 :             break;
    1395                 : 
    1396                 :         case GAC:
    1397              10 :             nRasterXSize = 409;
    1398              10 :             nBufferSize = 4092;
    1399              10 :             iGCPStart = 5; // FIXME: depends of scan direction
    1400              10 :             iGCPStep = 8;
    1401              10 :             nGCPsPerLine = 51;
    1402              10 :             if (  eL1BFormat == L1B_NOAA9 )
    1403                 :             {
    1404               7 :                 if (iDataFormat == PACKED10BIT)
    1405                 :                 {
    1406               3 :                     nRecordSize = 3220;
    1407               3 :                     nRecordDataEnd = 3176;
    1408                 :                 }
    1409               4 :                 else if (iDataFormat == UNPACKED16BIT)
    1410               2 :                     switch(nBands)
    1411                 :                     {
    1412                 :                         case 1:
    1413               0 :                         nRecordSize = 1268;
    1414               0 :                         nRecordDataEnd = 1266;
    1415               0 :                         break;
    1416                 :                         case 2:
    1417               1 :                         nRecordSize = 2084;
    1418               1 :                         nRecordDataEnd = 2084;
    1419               1 :                         break;
    1420                 :                         case 3:
    1421               0 :                         nRecordSize = 2904;
    1422               0 :                         nRecordDataEnd = 2902;
    1423               0 :                         break;
    1424                 :                         case 4:
    1425               0 :                         nRecordSize = 3720;
    1426               0 :                         nRecordDataEnd = 3720;
    1427               0 :                         break;
    1428                 :                         case 5:
    1429               1 :                         nRecordSize = 4540;
    1430               1 :                         nRecordDataEnd = 4538;
    1431                 :                         break;
    1432                 :                     }
    1433                 :                 else // UNPACKED8BIT
    1434                 :                 {
    1435               2 :                     switch(nBands)
    1436                 :                     {
    1437                 :                         case 1:
    1438               0 :                         nRecordSize = 860;
    1439               0 :                         nRecordDataEnd = 858;
    1440               0 :                         break;
    1441                 :                         case 2:
    1442               1 :                         nRecordSize = 1268;
    1443               1 :                         nRecordDataEnd = 1266;
    1444               1 :                         break;
    1445                 :                         case 3:
    1446               0 :                         nRecordSize = 1676;
    1447               0 :                         nRecordDataEnd = 1676;
    1448               0 :                         break;
    1449                 :                         case 4:
    1450               0 :                         nRecordSize = 2084;
    1451               0 :                         nRecordDataEnd = 2084;
    1452               0 :                         break;
    1453                 :                         case 5:
    1454               1 :                         nRecordSize = 2496;
    1455               1 :                         nRecordDataEnd = 2494;
    1456                 :                         break;
    1457                 :                     }
    1458                 :                 }
    1459               7 :                 nDataStartOffset = nRecordSize * 2 + L1B_NOAA9_HEADER_SIZE;
    1460               7 :                 nRecordDataStart = 448;
    1461               7 :                 iGCPCodeOffset = 52;
    1462               7 :                 iGCPOffset = 104;
    1463                 :             }
    1464                 : 
    1465               6 :             else if ( eL1BFormat == L1B_NOAA15
    1466                 :                       || eL1BFormat == L1B_NOAA15_NOHDR )
    1467                 :             {
    1468               3 :                 if (iDataFormat == PACKED10BIT)
    1469                 :                 {
    1470               1 :                     nRecordSize = 4608;
    1471               1 :                     nRecordDataEnd = 3992;
    1472                 :                 }
    1473               2 :                 else if (iDataFormat == UNPACKED16BIT)
    1474                 :                 {
    1475               1 :                     switch(nBands)
    1476                 :                     {
    1477                 :                         case 1:
    1478               0 :                         nRecordSize = 2360;
    1479               0 :                         nRecordDataEnd = 2082;
    1480               0 :                         break;
    1481                 :                         case 2:
    1482               0 :                         nRecordSize = 3176;
    1483               0 :                         nRecordDataEnd = 2900;
    1484               0 :                         break;
    1485                 :                         case 3:
    1486               0 :                         nRecordSize = 3992;
    1487               0 :                         nRecordDataEnd = 3718;
    1488               0 :                         break;
    1489                 :                         case 4:
    1490               0 :                         nRecordSize = 4816;
    1491               0 :                         nRecordDataEnd = 4536;
    1492               0 :                         break;
    1493                 :                         case 5:
    1494               1 :                         nRecordSize = 5632;
    1495               1 :                         nRecordDataEnd = 5354;
    1496                 :                         break;
    1497                 :                     }
    1498                 :                 }
    1499                 :                 else // UNPACKED8BIT
    1500                 :                 {
    1501               1 :                     switch(nBands)
    1502                 :                     {
    1503                 :                         case 1:
    1504               0 :                         nRecordSize = 1952;
    1505               0 :                         nRecordDataEnd = 1673;
    1506               0 :                         break;
    1507                 :                         case 2:
    1508               0 :                         nRecordSize = 2360;
    1509               0 :                         nRecordDataEnd = 2082;
    1510               0 :                         break;
    1511                 :                         case 3:
    1512               0 :                         nRecordSize = 2768;
    1513               0 :                         nRecordDataEnd = 2491;
    1514               0 :                         break;
    1515                 :                         case 4:
    1516               0 :                         nRecordSize = 3176;
    1517               0 :                         nRecordDataEnd = 2900;
    1518               0 :                         break;
    1519                 :                         case 5:
    1520               1 :                         nRecordSize = 3584;
    1521               1 :                         nRecordDataEnd = 3309;
    1522                 :                         break;
    1523                 :                     }
    1524                 :                 }
    1525                 :                 nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ?
    1526               3 :                     nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
    1527               3 :                 nRecordDataStart = 1264;
    1528               3 :                 iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
    1529               3 :                 iGCPOffset = 640;
    1530                 :             }
    1531                 :             else
    1532               0 :                 return 0;
    1533              10 :         break;
    1534                 :         default:
    1535               0 :             return 0;
    1536                 :     }
    1537                 : 
    1538              16 :     return 1;
    1539                 : }
    1540                 : 
    1541                 : /************************************************************************/
    1542                 : /*                           DetectFormat()                             */
    1543                 : /************************************************************************/
    1544                 : 
    1545           11447 : int L1BDataset::DetectFormat( GDALOpenInfo *poOpenInfo )
    1546                 : 
    1547                 : {
    1548           11447 :     GByte* pabyHeader = poOpenInfo->pabyHeader;
    1549           11447 :     if (pabyHeader == NULL || poOpenInfo->nHeaderBytes < L1B_NOAA9_HEADER_SIZE)
    1550           10709 :         return L1B_NONE;
    1551                 : 
    1552                 :     // We will try the NOAA-15 and later formats first
    1553             738 :     if ( poOpenInfo->nHeaderBytes > L1B_NOAA15_HEADER_SIZE + 61
    1554                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 25) == '.'
    1555                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 30) == '.'
    1556                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 33) == '.'
    1557                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 40) == '.'
    1558                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 46) == '.'
    1559                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 52) == '.'
    1560                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 61) == '.' )
    1561               5 :         return L1B_NOAA15;
    1562                 : 
    1563                 :     // Next try the NOAA-9/14 formats
    1564             733 :     if ( *(pabyHeader + 8 + 25) == '.'
    1565                 :          && *(pabyHeader + 8 + 30) == '.'
    1566                 :          && *(pabyHeader + 8 + 33) == '.'
    1567                 :          && *(pabyHeader + 8 + 40) == '.'
    1568                 :          && *(pabyHeader + 8 + 46) == '.'
    1569                 :          && *(pabyHeader + 8 + 52) == '.'
    1570                 :          && *(pabyHeader + 8 + 61) == '.' )
    1571               7 :         return L1B_NOAA9;
    1572                 : 
    1573                 :     // Finally try the AAPP formats 
    1574             726 :     if ( *(pabyHeader + 25) == '.'
    1575                 :          && *(pabyHeader + 30) == '.'
    1576                 :          && *(pabyHeader + 33) == '.'
    1577                 :          && *(pabyHeader + 40) == '.'
    1578                 :          && *(pabyHeader + 46) == '.'
    1579                 :          && *(pabyHeader + 52) == '.'
    1580                 :          && *(pabyHeader + 61) == '.' )
    1581               0 :         return L1B_NOAA15_NOHDR;
    1582                 : 
    1583             726 :     return L1B_NONE;
    1584                 : }
    1585                 : 
    1586                 : /************************************************************************/
    1587                 : /*                              Identify()                              */
    1588                 : /************************************************************************/
    1589                 : 
    1590               0 : int L1BDataset::Identify( GDALOpenInfo *poOpenInfo )
    1591                 : 
    1592                 : {
    1593               0 :     if ( DetectFormat(poOpenInfo) == L1B_NONE )
    1594               0 :         return FALSE;
    1595                 : 
    1596               0 :     return TRUE;
    1597                 : }
    1598                 : 
    1599                 : /************************************************************************/
    1600                 : /*                                Open()                                */
    1601                 : /************************************************************************/
    1602                 : 
    1603           11447 : GDALDataset *L1BDataset::Open( GDALOpenInfo * poOpenInfo )
    1604                 : 
    1605                 : {
    1606           11447 :     int     eL1BFormat = DetectFormat( poOpenInfo );
    1607           11447 :     if ( eL1BFormat == L1B_NONE )
    1608           11435 :         return NULL;
    1609                 :         
    1610                 : /* -------------------------------------------------------------------- */
    1611                 : /*      Confirm the requested access is supported.                      */
    1612                 : /* -------------------------------------------------------------------- */
    1613              12 :     if( poOpenInfo->eAccess == GA_Update )
    1614                 :     {
    1615                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1616                 :                   "The L1B driver does not support update access to existing"
    1617               0 :                   " datasets.\n" );
    1618               0 :         return NULL;
    1619                 :     }
    1620                 :     
    1621                 : #if 0
    1622                 :     Geolocation
    1623                 :     if ( EQUAL( poOpenInfo->pszFilename, "L1BGCPS:" ) )
    1624                 :         bFetchGeolocation = TRUE;
    1625                 : #endif
    1626                 : 
    1627                 : /* -------------------------------------------------------------------- */
    1628                 : /*      Create a corresponding GDALDataset.                             */
    1629                 : /* -------------------------------------------------------------------- */
    1630                 :     L1BDataset  *poDS;
    1631                 :     VSIStatBufL  sStat;
    1632              12 :     const char  *pszFilename = poOpenInfo->pszFilename;
    1633                 : 
    1634              12 :     poDS = new L1BDataset( eL1BFormat );
    1635                 : 
    1636              12 :     poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
    1637              12 :     if ( !poDS->fp )
    1638                 :     {
    1639               0 :         CPLDebug( "L1B", "Can't open file \"%s\".", poOpenInfo->pszFilename );
    1640               0 :         goto bad;
    1641                 :     }
    1642                 :     
    1643                 : /* -------------------------------------------------------------------- */
    1644                 : /*      Read the header.                                                */
    1645                 : /* -------------------------------------------------------------------- */
    1646              12 :     if ( poDS->ProcessDatasetHeader() != CE_None )
    1647                 :     {
    1648               0 :         CPLDebug( "L1B", "Error reading L1B record header." );
    1649               0 :         goto bad;
    1650                 :     }
    1651                 : 
    1652              12 :     VSIStatL(pszFilename, &sStat);
    1653                 : 
    1654              12 :     if ( poDS->bGuessDataFormat )
    1655                 :     {
    1656                 :         int nTempYSize;
    1657                 :         GUInt16 nScanlineNumber;
    1658                 :         int j;
    1659                 : 
    1660                 :         /* If the data format is unspecified, try each one of the 3 known data formats */
    1661                 :         /* It is considered valid when the spacing between the first 5 scanline numbers */
    1662                 :         /* is a constant */
    1663                 : 
    1664               8 :         for(j=0;j<3;j++)
    1665                 :         {
    1666               8 :             poDS->iDataFormat = PACKED10BIT + j;
    1667               8 :             if (!poDS->ComputeFileOffsets())
    1668               0 :                 goto bad;
    1669                 : 
    1670               8 :             nTempYSize = (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
    1671               8 :             if (nTempYSize < 5)
    1672               1 :                 continue;
    1673                 : 
    1674               7 :             int nLastScanlineNumber = 0;
    1675               7 :             int nDiffLine = 0;
    1676                 :             int i;
    1677              33 :             for (i=0;i<5;i++)
    1678                 :             {
    1679              29 :                 nScanlineNumber = 0;
    1680                 : 
    1681              29 :                 VSIFSeekL(poDS->fp, poDS->nDataStartOffset + i * poDS->nRecordSize, SEEK_SET);
    1682              29 :                 VSIFReadL(&nScanlineNumber, 1, 2, poDS->fp);
    1683                 : #ifdef CPL_LSB
    1684              29 :                 CPL_SWAP16PTR( &nScanlineNumber );
    1685                 : #endif
    1686              29 :                 if (i == 1)
    1687                 :                 {
    1688               7 :                     nDiffLine = nScanlineNumber - nLastScanlineNumber;
    1689               7 :                     if (nDiffLine == 0)
    1690               0 :                         break;
    1691                 :                 }
    1692              22 :                 else if (i > 1)
    1693                 :                 {
    1694              15 :                     if (nDiffLine != nScanlineNumber - nLastScanlineNumber)
    1695               3 :                         break;
    1696                 :                 }
    1697                 : 
    1698              26 :                 nLastScanlineNumber = nScanlineNumber;
    1699                 :             }
    1700                 : 
    1701               7 :             if (i == 5)
    1702                 :             {
    1703                 :                 CPLDebug("L1B", "Guessed data format : %s",
    1704                 :                          (poDS->iDataFormat == PACKED10BIT) ? "10" :
    1705               4 :                          (poDS->iDataFormat == UNPACKED8BIT) ? "08" : "16");
    1706               4 :                 break;
    1707                 :             }
    1708                 :         }
    1709                 : 
    1710               4 :         if (j == 3)
    1711                 :         {
    1712               0 :             CPLError(CE_Failure, CPLE_AppDefined, "Could not guess data format of L1B product");
    1713               0 :             goto bad;
    1714                 :         }
    1715                 :     }
    1716                 :     else
    1717                 :     {
    1718               8 :         if (!poDS->ComputeFileOffsets())
    1719               0 :             goto bad;
    1720                 :     }
    1721                 : 
    1722                 :     // Compute number of lines dinamycally, so we can read partially
    1723                 :     // downloaded files
    1724                 :     poDS->nRasterYSize =
    1725              12 :         (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
    1726                 : 
    1727                 : /* -------------------------------------------------------------------- */
    1728                 : /*      Create band information objects.                                */
    1729                 : /* -------------------------------------------------------------------- */
    1730                 :     int iBand, i;
    1731                 :     
    1732              65 :     for( iBand = 1, i = 0; iBand <= poDS->nBands; iBand++ )
    1733                 :     {
    1734              53 :         poDS->SetBand( iBand, new L1BRasterBand( poDS, iBand ));
    1735                 :         
    1736                 :         // Channels descriptions
    1737             106 :         if ( poDS->eSpacecraftID >= NOAA6 && poDS->eSpacecraftID <= METOP2 )
    1738                 :         {
    1739              53 :             if ( !(i & 0x01) && poDS->iChannelsMask & 0x01 )
    1740                 :             {
    1741              10 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[0] );
    1742              10 :                 i |= 0x01;
    1743              10 :                 continue;
    1744                 :             }
    1745              43 :             if ( !(i & 0x02) && poDS->iChannelsMask & 0x02 )
    1746                 :             {
    1747              12 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[1] );
    1748              12 :                 i |= 0x02;
    1749              12 :                 continue;
    1750                 :             }
    1751              31 :             if ( !(i & 0x04) && poDS->iChannelsMask & 0x04 )
    1752                 :             {
    1753              15 :                 if ( poDS->eSpacecraftID >= NOAA15
    1754                 :                      && poDS->eSpacecraftID <= METOP2 )
    1755               5 :                     if ( poDS->iInstrumentStatus & 0x0400 )
    1756               5 :                         poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[7] );
    1757                 :                     else
    1758               0 :                         poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[6] );
    1759                 :                 else    
    1760               5 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[2] );
    1761              10 :                 i |= 0x04;
    1762              10 :                 continue;
    1763                 :             }
    1764              21 :             if ( !(i & 0x08) && poDS->iChannelsMask & 0x08 )
    1765                 :             {
    1766              11 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] );
    1767              11 :                 i |= 0x08;
    1768              11 :                 continue;
    1769                 :             }
    1770              10 :             if ( !(i & 0x10) && poDS->iChannelsMask & 0x10 )
    1771                 :             {
    1772              10 :                 if (poDS->eSpacecraftID == NOAA13)              // 5 NOAA-13
    1773               0 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[5] );
    1774              10 :                 else if (poDS->eSpacecraftID == NOAA6 ||
    1775                 :                          poDS->eSpacecraftID == NOAA8 ||
    1776                 :                          poDS->eSpacecraftID == NOAA10)         // 4 NOAA-6,-8,-10
    1777               0 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] );
    1778                 :                 else
    1779              10 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[4] );
    1780              10 :                 i |= 0x10;
    1781              10 :                 continue;
    1782                 :             }
    1783                 :         }
    1784                 :     }
    1785                 : 
    1786                 : /* -------------------------------------------------------------------- */
    1787                 : /*      Do we have GCPs?                                                */
    1788                 : /* -------------------------------------------------------------------- */
    1789                 :     if ( 1/*EQUALN((const char *)pabyTBMHeader + 96, "Y", 1)*/ )
    1790                 :     {
    1791              12 :         poDS->ProcessRecordHeaders();
    1792                 : 
    1793                 : #if 0
    1794                 :     Geolocation
    1795                 :         CPLString  osTMP;
    1796                 : 
    1797                 :         poDS->SetMetadataItem( "SRS", poDS->pszGCPProjection, "GEOLOCATION" );
    1798                 :         
    1799                 :         osTMP.Printf( "L1BGCPS:\"%s\"", pszFilename );
    1800                 :         poDS->SetMetadataItem( "X_DATASET", osTMP, "GEOLOCATION" );
    1801                 :         poDS->SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" );
    1802                 :         poDS->SetMetadataItem( "Y_DATASET", osTMP, "GEOLOCATION" );
    1803                 :         poDS->SetMetadataItem( "Y_BAND", "2" , "GEOLOCATION" );
    1804                 : 
    1805                 :         osTMP.Printf( "%d", (poDS->eLocationIndicator == DESCEND) ?
    1806                 :             poDS->iGCPStart : (poDS->nRasterXSize - poDS->iGCPStart) );
    1807                 :         poDS->SetMetadataItem( "PIXEL_OFFSET", osTMP, "GEOLOCATION" );
    1808                 :         osTMP.Printf( "%d", (poDS->eLocationIndicator == DESCEND) ?
    1809                 :                       poDS->iGCPStep : -poDS->iGCPStep );
    1810                 :         poDS->SetMetadataItem( "PIXEL_STEP", osTMP, "GEOLOCATION" );
    1811                 : 
    1812                 :         poDS->SetMetadataItem( "LINE_OFFSET", "0", "GEOLOCATION" );
    1813                 :         poDS->SetMetadataItem( "LINE_STEP", "1", "GEOLOCATION" );
    1814                 : #endif
    1815                 :     }
    1816                 : 
    1817                 : /* -------------------------------------------------------------------- */
    1818                 : /*      Initialize any PAM information.                                 */
    1819                 : /* -------------------------------------------------------------------- */
    1820              12 :     poDS->SetDescription( poOpenInfo->pszFilename );
    1821              12 :     poDS->TryLoadXML();
    1822                 : 
    1823                 : /* -------------------------------------------------------------------- */
    1824                 : /*      Check for external overviews.                                   */
    1825                 : /* -------------------------------------------------------------------- */
    1826              12 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
    1827                 : 
    1828              12 :     return( poDS );
    1829                 : 
    1830                 : bad:
    1831               0 :     delete poDS;
    1832               0 :     return NULL;
    1833                 : }
    1834                 : 
    1835                 : /************************************************************************/
    1836                 : /*                        GDALRegister_L1B()                            */
    1837                 : /************************************************************************/
    1838                 : 
    1839             558 : void GDALRegister_L1B()
    1840                 : 
    1841                 : {
    1842                 :     GDALDriver  *poDriver;
    1843                 : 
    1844             558 :     if( GDALGetDriverByName( "L1B" ) == NULL )
    1845                 :     {
    1846             537 :         poDriver = new GDALDriver();
    1847                 :         
    1848             537 :         poDriver->SetDescription( "L1B" );
    1849                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
    1850             537 :                                    "NOAA Polar Orbiter Level 1b Data Set" );
    1851                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
    1852             537 :                                    "frmt_l1b.html" );
    1853                 : 
    1854             537 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
    1855                 : 
    1856             537 :         poDriver->pfnOpen = L1BDataset::Open;
    1857                 : 
    1858             537 :         GetGDALDriverManager()->RegisterDriver( poDriver );
    1859                 :     }
    1860             558 : }
    1861                 : 

Generated by: LCOV version 1.7