LCOV - code coverage report
Current view: directory - frmts/l1b - l1bdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 714 502 70.3 %
Date: 2010-01-09 Functions: 24 22 91.7 %

       1                 : /******************************************************************************
       2                 :  * $Id: l1bdataset.cpp 17664 2009-09-21 21:16:45Z 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 17664 2009-09-21 21:16:45Z 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                 :     FILE        *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             106 : {
     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              24 : 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              24 : }
     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                 :                   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 *)CPLCalloc( nTargetLines * nGCPsPerLine,
     650              12 :                                         sizeof(GDAL_GCP) );
     651              12 :     GDALInitGCPs( nTargetLines * nGCPsPerLine, pasGCPList );
     652                 : 
     653                 : /* -------------------------------------------------------------------- */
     654                 : /*      Fetch the GCPs for each selected line.  We force the last       */
     655                 : /*      line sampled to be the last line in the dataset even if that    */
     656                 : /*      leaves a bigger than expected gap.                              */
     657                 : /* -------------------------------------------------------------------- */
     658                 :     int iStep;
     659                 : 
     660             252 :     for( iStep = 0; iStep < nTargetLines; iStep++ )
     661                 :     {
     662             240 :         int nOrigGCPs = nGCPCount;
     663                 :         int iLine;
     664                 : 
     665             240 :         if( iStep == nTargetLines - 1 )
     666              12 :             iLine = nRasterXSize - 1;
     667                 :         else
     668             228 :             iLine = nLineSkip * iStep;
     669                 : 
     670             240 :         VSIFSeekL( fp, nDataStartOffset + iLine * nRecordSize, SEEK_SET );
     671             240 :         VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp );
     672                 : 
     673             240 :         FetchGCPs( pasGCPList, (GByte *)pRecordHeader, iLine );
     674                 : 
     675                 : /* -------------------------------------------------------------------- */
     676                 : /*      We don't really want too many GCPs per line.  Downsample to     */
     677                 : /*      11 per line.                                                    */
     678                 : /* -------------------------------------------------------------------- */
     679                 :         int iGCP;
     680             240 :         int nGCPsOnThisLine = nGCPCount - nOrigGCPs;
     681             240 :         int nDesiredGCPsPerLine = MIN(DESIRED_GCPS_PER_LINE,nGCPsOnThisLine);
     682                 :         int nGCPStep = ( nDesiredGCPsPerLine > 1 ) ?
     683             240 :             ( nGCPsOnThisLine - 1 ) / ( nDesiredGCPsPerLine-1 ) : 1;
     684             240 :         int iSrcGCP = nOrigGCPs;
     685             240 :         int iDstGCP = nOrigGCPs;
     686                 : 
     687             240 :         if( nGCPStep == 0 )
     688               0 :             nGCPStep = 1;
     689                 : 
     690            2880 :         for( iGCP = 0; iGCP < nDesiredGCPsPerLine; iGCP++ )
     691                 :         {
     692            2640 :             iSrcGCP += iGCP * nGCPStep;
     693            2640 :             iDstGCP += iGCP;
     694                 : 
     695            2640 :             pasGCPList[iDstGCP].dfGCPX = pasGCPList[iSrcGCP].dfGCPX;
     696            2640 :             pasGCPList[iDstGCP].dfGCPY = pasGCPList[iSrcGCP].dfGCPY;
     697            2640 :             pasGCPList[iDstGCP].dfGCPPixel = pasGCPList[iSrcGCP].dfGCPPixel;
     698            2640 :             pasGCPList[iDstGCP].dfGCPLine = pasGCPList[iSrcGCP].dfGCPLine;
     699                 :         }
     700                 : 
     701             240 :         nGCPCount = nOrigGCPs + nDesiredGCPsPerLine;
     702                 :     }
     703                 : 
     704              12 :     if( nGCPCount < nTargetLines * nGCPsPerLine )
     705                 :     {
     706                 :         GDALDeinitGCPs( nTargetLines * nGCPsPerLine - nGCPCount, 
     707              12 :                         pasGCPList + nGCPCount );
     708                 :     }
     709                 : 
     710              12 :     CPLFree( pRecordHeader );
     711                 : 
     712                 : /* -------------------------------------------------------------------- */
     713                 : /*      Set fetched information as metadata records                     */
     714                 : /* -------------------------------------------------------------------- */
     715                 :     // Time of first scanline
     716              12 :     SetMetadataItem( "START",  sStartTime.PrintTime() );
     717                 :     // Time of last scanline
     718              12 :     SetMetadataItem( "STOP",  sStopTime.PrintTime() );
     719                 :     // AVHRR Earth location indication
     720                 : 
     721              12 :     switch( eLocationIndicator )
     722                 :     {
     723                 :         case ASCEND:
     724               4 :             SetMetadataItem( "LOCATION", "Ascending" );
     725               4 :             break;
     726                 :         case DESCEND:
     727                 :         default:
     728               8 :             SetMetadataItem( "LOCATION", "Descending" );
     729                 :             break;
     730                 :     }
     731                 : 
     732              12 : }
     733                 : 
     734                 : /************************************************************************/
     735                 : /*                      ProcessDatasetHeader()                          */
     736                 : /************************************************************************/
     737                 : 
     738              12 : CPLErr L1BDataset::ProcessDatasetHeader()
     739                 : {
     740                 :     char    szDatasetName[L1B_DATASET_NAME_SIZE + 1];
     741                 : 
     742              12 :     if ( eL1BFormat == L1B_NOAA9 )
     743                 :     {
     744                 :         GByte   abyTBMHeader[L1B_NOAA9_HEADER_SIZE];
     745                 : 
     746               7 :         if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0
     747                 :              || VSIFReadL( abyTBMHeader, 1, L1B_NOAA9_HEADER_SIZE,
     748                 :                            fp ) < L1B_NOAA9_HEADER_SIZE )
     749                 :         {
     750               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 TBM header." );
     751               0 :             return CE_Failure;
     752                 :         }
     753                 : 
     754                 :         // Fetch dataset name. NOAA-9/14 datasets contain the names in TBM
     755                 :         // header only, so read it there.
     756                 :         memcpy( szDatasetName, abyTBMHeader + L1B_NOAA9_HDR_NAME_OFF,
     757               7 :                 L1B_DATASET_NAME_SIZE );
     758               7 :         szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
     759                 : 
     760                 :         // Determine processing center where the dataset was created
     761               7 :         if ( EQUALN((const char *)abyTBMHeader
     762                 :                     + L1B_NOAA9_HDR_NAME_OFF, "CMS", 3) )
     763               0 :              eProcCenter = CMS;
     764               7 :         else if ( EQUALN((const char *)abyTBMHeader
     765                 :                          + L1B_NOAA9_HDR_NAME_OFF, "DSS", 3) )
     766               0 :              eProcCenter = DSS;
     767               7 :         else if ( EQUALN((const char *)abyTBMHeader
     768                 :                          + L1B_NOAA9_HDR_NAME_OFF, "NSS", 3) )
     769               7 :              eProcCenter = NSS;
     770               0 :         else if ( EQUALN((const char *)abyTBMHeader
     771                 :                          + L1B_NOAA9_HDR_NAME_OFF, "UKM", 3) )
     772               0 :              eProcCenter = UKM;
     773                 :         else
     774               0 :              eProcCenter = UNKNOWN_CENTER;
     775                 : 
     776                 :         // Determine number of bands
     777                 :         int     i;
     778             147 :         for ( i = 0; i < L1B_NOAA9_HDR_CHAN_SIZE; i++ )
     779                 :         {
     780             267 :             if ( abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 1
     781             127 :                  || abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 'Y' )
     782                 :             {
     783              13 :                 nBands++;
     784              13 :                 iChannelsMask |= (1 << i);
     785                 :             }
     786                 :         }
     787               7 :         if ( nBands == 0 || nBands > 5 )
     788                 :         {
     789               3 :             nBands = 5;
     790               3 :             iChannelsMask = 0x1F;
     791                 :         }
     792                 : 
     793                 :         // Determine data format (10-bit packed or 8/16-bit unpacked)
     794               7 :         if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     795                 :                     "10", 2) )
     796               1 :             iDataFormat = PACKED10BIT;
     797               6 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     798                 :                          "16", 2) )
     799               1 :             iDataFormat = UNPACKED16BIT;
     800               5 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     801                 :                          "08", 2) )
     802               1 :             iDataFormat = UNPACKED8BIT;
     803              12 :         else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
     804                 :                          "  ", 2)
     805               4 :                   || abyTBMHeader[L1B_NOAA9_HDR_WORD_OFF] == '\0' )
     806                 :             /* Empty string can be found in the following samples : 
     807                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/franh.1b (10 bit)
     808                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/frang.1b (10 bit)
     809                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/calfilel.1b (16 bit)
     810                 :                 http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/rapnzg.1b (16 bit)
     811                 :                 ftp://ftp.sat.dundee.ac.uk/misc/testdata/noaa12/hrptnoaa1b.dat (10 bit)
     812                 :             */
     813               4 :             bGuessDataFormat = TRUE;
     814                 :         else
     815                 :         {
     816                 : #ifdef DEBUG
     817                 :             CPLDebug( "L1B", "Unknown data format \"%.2s\".",
     818                 :                       abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF );
     819                 : #endif
     820               0 :             return CE_Failure;
     821                 :         }
     822                 : 
     823                 :         // Now read the dataset header record
     824                 :         GByte   abyRecHeader[L1B_NOAA9_HDR_REC_SIZE];
     825               7 :         if ( VSIFSeekL( fp, L1B_NOAA9_HEADER_SIZE, SEEK_SET ) < 0
     826                 :              || VSIFReadL( abyRecHeader, 1, L1B_NOAA9_HDR_REC_SIZE,
     827                 :                            fp ) < L1B_NOAA9_HDR_REC_SIZE )
     828                 :         {
     829               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 record header." );
     830               0 :             return CE_Failure;
     831                 :         }
     832                 : 
     833                 :         // Determine the spacecraft name
     834               7 :         switch ( abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] )
     835                 :         {
     836                 :             /* FIXME: use time code to determine TIROS-N, because the SatID
     837                 :              * identical to NOAA-11
     838                 :              * case 1:
     839                 :                 eSpacecraftID = TIROSN;
     840                 :                 break;
     841                 :             case 2:
     842                 :                 eSpacecraftID = NOAA6;
     843                 :                 break;*/
     844                 :             case 4:
     845               0 :                 eSpacecraftID = NOAA7;
     846               0 :                 break;
     847                 :             case 6:
     848               0 :                 eSpacecraftID = NOAA8;
     849               0 :                 break;
     850                 :             case 7:
     851               0 :                 eSpacecraftID = NOAA9;
     852               0 :                 break;
     853                 :             case 8:
     854               0 :                 eSpacecraftID = NOAA10;
     855               0 :                 break;
     856                 :             case 1:
     857               0 :                 eSpacecraftID = NOAA11;
     858               0 :                 break;
     859                 :             case 5:
     860               4 :                 eSpacecraftID = NOAA12;
     861               4 :                 break;
     862                 :             case 2:
     863               0 :                 eSpacecraftID = NOAA13;
     864               0 :                 break;
     865                 :             case 3:
     866               3 :                 eSpacecraftID = NOAA14;
     867               3 :                 break;
     868                 :             default:
     869                 : #ifdef DEBUG
     870                 :                 CPLDebug( "L1B", "Unknown spacecraft ID \"%d\".",
     871                 :                           abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] );
     872                 : #endif
     873               0 :                 return CE_Failure;
     874                 :         }
     875                 : 
     876                 :         // Determine the product data type
     877               7 :         int iWord = abyRecHeader[L1B_NOAA9_HDR_REC_PROD_OFF] >> 4;
     878               7 :         switch ( iWord )
     879                 :         {
     880                 :             case 1:
     881               1 :                 eProductType = LAC;
     882               1 :                 break;
     883                 :             case 2:
     884               5 :                 eProductType = GAC;
     885               5 :                 break;
     886                 :             case 3:
     887               1 :                 eProductType = HRPT;
     888               1 :                 break;
     889                 :             default:
     890                 : #ifdef DEBUG
     891                 :                 CPLDebug( "L1B", "Unknown product type \"%d\".", iWord );
     892                 : #endif
     893               0 :                 return CE_Failure;
     894                 :         }
     895                 : 
     896                 :         // Determine receiving station name
     897               7 :         iWord = ( abyRecHeader[L1B_NOAA9_HDR_REC_DSTAT_OFF] & 0x60 ) >> 5;
     898               7 :         switch( iWord )
     899                 :         {
     900                 :             case 1:
     901               1 :                 eSource = GC;
     902               1 :                 break;
     903                 :             case 2:
     904               6 :                 eSource = WI;
     905               6 :                 break;
     906                 :             case 3:
     907               0 :                 eSource = SO;
     908               0 :                 break;
     909                 :             default:
     910               0 :                 eSource = UNKNOWN_STATION;
     911                 :                 break;
     912                 :         }
     913                 :     }
     914                 : 
     915              10 :     else if ( eL1BFormat == L1B_NOAA15 || eL1BFormat == L1B_NOAA15_NOHDR )
     916                 :     {
     917               5 :         if ( eL1BFormat == L1B_NOAA15 )
     918                 :         {
     919                 :             GByte   abyARSHeader[L1B_NOAA15_HEADER_SIZE];
     920                 : 
     921               5 :             if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0
     922                 :                  || VSIFReadL( abyARSHeader, 1, L1B_NOAA15_HEADER_SIZE,
     923                 :                                fp ) < L1B_NOAA15_HEADER_SIZE )
     924                 :             {
     925               0 :                 CPLDebug( "L1B", "Can't read NOAA-15 ARS header." );
     926               0 :                 return CE_Failure;
     927                 :             }
     928                 : 
     929                 :             // Determine number of bands
     930                 :             int     i;
     931             105 :             for ( i = 0; i < L1B_NOAA15_HDR_CHAN_SIZE; i++ )
     932                 :             {
     933             200 :                 if ( abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 1
     934             100 :                      || abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 'Y' )
     935                 :                 {
     936              70 :                     nBands++;
     937              70 :                     iChannelsMask |= (1 << i);
     938                 :                 }
     939                 :             }
     940               5 :             if ( nBands == 0 || nBands > 5 )
     941                 :             {
     942               3 :                 nBands = 5;
     943               3 :                 iChannelsMask = 0x1F;
     944                 :             }
     945                 : 
     946                 :             // Determine data format (10-bit packed or 8/16-bit unpacked)
     947               5 :             if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     948                 :                         "10", 2) )
     949               3 :                 iDataFormat = PACKED10BIT;
     950               2 :             else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     951                 :                              "16", 2) )
     952               1 :                 iDataFormat = UNPACKED16BIT;
     953               1 :             else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
     954                 :                              "08", 2) )
     955               1 :                 iDataFormat = UNPACKED8BIT;
     956                 :             else
     957                 :             {
     958                 : #ifdef DEBUG
     959                 :                 CPLDebug( "L1B", "Unknown data format \"%.2s\".",
     960                 :                           abyARSHeader + L1B_NOAA9_HDR_WORD_OFF );
     961                 : #endif
     962               0 :                 return CE_Failure;
     963                 :             }
     964                 :         }
     965                 :         else
     966                 :         {
     967               0 :             nBands = 5;
     968               0 :             iChannelsMask = 0x1F;
     969               0 :             iDataFormat = PACKED10BIT;
     970                 :         }
     971                 : 
     972                 :         // Now read the dataset header record
     973                 :         GByte   abyRecHeader[L1B_NOAA15_HDR_REC_SIZE];
     974               5 :         if ( VSIFSeekL( fp,
     975                 :                         (eL1BFormat == L1B_NOAA15) ? L1B_NOAA15_HEADER_SIZE : 0,
     976                 :                         SEEK_SET ) < 0
     977                 :              || VSIFReadL( abyRecHeader, 1, L1B_NOAA15_HDR_REC_SIZE,
     978                 :                            fp ) < L1B_NOAA15_HDR_REC_SIZE )
     979                 :         {
     980               0 :             CPLDebug( "L1B", "Can't read NOAA-9/14 record header." );
     981               0 :             return CE_Failure;
     982                 :         }
     983                 : 
     984                 :         // Fetch dataset name
     985                 :         memcpy( szDatasetName, abyRecHeader + L1B_NOAA15_HDR_REC_NAME_OFF,
     986               5 :                 L1B_DATASET_NAME_SIZE );
     987               5 :         szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
     988                 : 
     989                 :         // Determine processing center where the dataset was created
     990               5 :         if ( EQUALN((const char *)abyRecHeader
     991                 :                     + L1B_NOAA15_HDR_REC_SITE_OFF, "CMS", 3) )
     992               0 :              eProcCenter = CMS;
     993               5 :         else if ( EQUALN((const char *)abyRecHeader
     994                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "DSS", 3) )
     995               2 :              eProcCenter = DSS;
     996               3 :         else if ( EQUALN((const char *)abyRecHeader
     997                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "NSS", 3) )
     998               3 :              eProcCenter = NSS;
     999               0 :         else if ( EQUALN((const char *)abyRecHeader
    1000                 :                          + L1B_NOAA15_HDR_REC_SITE_OFF, "UKM", 3) )
    1001               0 :              eProcCenter = UKM;
    1002                 :         else
    1003               0 :              eProcCenter = UNKNOWN_CENTER;
    1004                 : 
    1005                 :         // Determine the spacecraft name
    1006               5 :         int iWord = CPL_MSBWORD16( *(GUInt16 *)
    1007                 :             (abyRecHeader + L1B_NOAA15_HDR_REC_ID_OFF) );
    1008               5 :         switch ( iWord )
    1009                 :         {
    1010                 :             case 2:
    1011               1 :                 eSpacecraftID = NOAA16;
    1012               1 :                 break;
    1013                 :             case 4:
    1014               1 :                 eSpacecraftID = NOAA15;
    1015               1 :                 break;
    1016                 :             case 6:
    1017               1 :                 eSpacecraftID = NOAA17;
    1018               1 :                 break;
    1019                 :             case 7:
    1020               1 :                 eSpacecraftID = NOAA18;
    1021               1 :                 break;
    1022                 :             /* FIXME: find appropriate samples and test these two cases:
    1023                 :              * case 8:
    1024                 :                 eSpacecraftID = NOAA-N';
    1025                 :                 break;
    1026                 :             case 11:
    1027                 :                 eSpacecraftID = METOP-1;
    1028                 :                 break;*/
    1029                 :             case 12:
    1030                 :             case 14:    // METOP simulator (code used in AAPP format)
    1031               1 :                 eSpacecraftID = METOP2;
    1032               1 :                 break;
    1033                 :             default:
    1034                 : #ifdef DEBUG
    1035                 :                 CPLDebug( "L1B", "Unknown spacecraft ID \"%d\".", iWord );
    1036                 : #endif
    1037               0 :                 return CE_Failure;
    1038                 :         }
    1039                 : 
    1040                 :         // Determine the product data type
    1041                 :         iWord = CPL_MSBWORD16( *(GUInt16 *)
    1042               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_PROD_OFF) );
    1043               5 :         switch ( iWord )
    1044                 :         {
    1045                 :             case 1:
    1046               0 :                 eProductType = LAC;
    1047               0 :                 break;
    1048                 :             case 2:
    1049               3 :                 eProductType = GAC;
    1050               3 :                 break;
    1051                 :             case 3:
    1052               1 :                 eProductType = HRPT;
    1053               1 :                 break;
    1054                 :             case 4:     // XXX: documentation specifies the code '4'
    1055                 :             case 13:    // for FRAC but real datasets contain '13 here.'
    1056               1 :                 eProductType = FRAC;
    1057               1 :                 break;
    1058                 :             default:
    1059                 : #ifdef DEBUG
    1060                 :                 CPLDebug( "L1B", "Unknown product type \"%d\".", iWord );
    1061                 : #endif
    1062               0 :                 return CE_Failure;
    1063                 :         }
    1064                 : 
    1065                 :         // Fetch hinstrument status. Helps to determine whether we have
    1066                 :         // 3A or 3B channel in the dataset.
    1067                 :         iInstrumentStatus = CPL_MSBWORD32( *(GUInt32 *)
    1068               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_STAT_OFF) );
    1069                 : 
    1070                 :         // Determine receiving station name
    1071                 :         iWord = CPL_MSBWORD16( *(GUInt16 *)
    1072               5 :             (abyRecHeader + L1B_NOAA15_HDR_REC_SRC_OFF) );
    1073               5 :         switch( iWord )
    1074                 :         {
    1075                 :             case 1:
    1076               2 :                 eSource = GC;
    1077               2 :                 break;
    1078                 :             case 2:
    1079               1 :                 eSource = WI;
    1080               1 :                 break;
    1081                 :             case 3:
    1082               0 :                 eSource = SO;
    1083               0 :                 break;
    1084                 :             case 4:
    1085               0 :                 eSource = SV;
    1086               0 :                 break;
    1087                 :             case 5:
    1088               0 :                 eSource = MO;
    1089               0 :                 break;
    1090                 :             default:
    1091               2 :                 eSource = UNKNOWN_STATION;
    1092                 :                 break;
    1093                 :         }
    1094                 :     }
    1095                 :     else
    1096               0 :         return CE_Failure;
    1097                 : 
    1098                 : /* -------------------------------------------------------------------- */
    1099                 : /*      Set fetched information as metadata records                     */
    1100                 : /* -------------------------------------------------------------------- */
    1101                 :     const char *pszText;
    1102                 : 
    1103              12 :     SetMetadataItem( "DATASET_NAME",  szDatasetName );
    1104                 : 
    1105              12 :     switch( eSpacecraftID )
    1106                 :     {
    1107                 :         case TIROSN:
    1108               0 :             pszText = "TIROS-N";
    1109               0 :             break;
    1110                 :         case NOAA6:
    1111               0 :             pszText = "NOAA-6(A)";
    1112               0 :             break;
    1113                 :         case NOAAB:
    1114               0 :             pszText = "NOAA-B";
    1115               0 :             break;
    1116                 :         case NOAA7:
    1117               0 :             pszText = "NOAA-7(C)";
    1118               0 :             break;
    1119                 :         case NOAA8:
    1120               0 :             pszText = "NOAA-8(E)";
    1121               0 :             break;
    1122                 :         case NOAA9:
    1123               0 :             pszText = "NOAA-9(F)";
    1124               0 :             break;
    1125                 :         case NOAA10:
    1126               0 :             pszText = "NOAA-10(G)";
    1127               0 :             break;
    1128                 :         case NOAA11:
    1129               0 :             pszText = "NOAA-11(H)";
    1130               0 :             break;
    1131                 :         case NOAA12:
    1132               4 :             pszText = "NOAA-12(D)";
    1133               4 :             break;
    1134                 :         case NOAA13:
    1135               0 :             pszText = "NOAA-13(I)";
    1136               0 :             break;
    1137                 :         case NOAA14:
    1138               3 :             pszText = "NOAA-14(J)";
    1139               3 :             break;
    1140                 :         case NOAA15:
    1141               1 :             pszText = "NOAA-15(K)";
    1142               1 :             break;
    1143                 :         case NOAA16:
    1144               1 :             pszText = "NOAA-16(L)";
    1145               1 :             break;
    1146                 :         case NOAA17:
    1147               1 :             pszText = "NOAA-17(M)";
    1148               1 :             break;
    1149                 :         case NOAA18:
    1150               1 :             pszText = "NOAA-18(N)";
    1151               1 :             break;
    1152                 :         case METOP2:
    1153               1 :             pszText = "METOP-2(A)";
    1154               1 :             break;
    1155                 :         default:
    1156               0 :             pszText = "Unknown";
    1157                 :             break;
    1158                 :     }
    1159              12 :     SetMetadataItem( "SATELLITE",  pszText );
    1160                 : 
    1161              12 :     switch( eProductType )
    1162                 :     {
    1163                 :         case LAC:
    1164               1 :             pszText = "AVHRR LAC";
    1165               1 :             break;
    1166                 :         case HRPT:
    1167               2 :             pszText = "AVHRR HRPT";
    1168               2 :             break;
    1169                 :         case GAC:
    1170               8 :             pszText = "AVHRR GAC";
    1171               8 :             break;
    1172                 :         case FRAC:
    1173               1 :             pszText = "AVHRR FRAC";
    1174               1 :             break;
    1175                 :         default:
    1176               0 :             pszText = "Unknown";
    1177                 :             break;
    1178                 :     }
    1179              12 :     SetMetadataItem( "DATA_TYPE",  pszText );
    1180                 : 
    1181                 :     // Get revolution number as string, we don't need this value for processing
    1182                 :     char    szRevolution[6];
    1183              12 :     memcpy( szRevolution, szDatasetName + 32, 5 );
    1184              12 :     szRevolution[5] = '\0';
    1185              12 :     SetMetadataItem( "REVOLUTION",  szRevolution );
    1186                 : 
    1187              12 :     switch( eSource )
    1188                 :     {
    1189                 :         case DU:
    1190               0 :             pszText = "Dundee, Scotland, UK";
    1191               0 :             break;
    1192                 :         case GC:
    1193               3 :             pszText = "Fairbanks, Alaska, USA (formerly Gilmore Creek)";
    1194               3 :             break;
    1195                 :         case HO:
    1196               0 :             pszText = "Honolulu, Hawaii, USA";
    1197               0 :             break;
    1198                 :         case MO:
    1199               0 :             pszText = "Monterey, California, USA";
    1200               0 :             break;
    1201                 :         case WE:
    1202               0 :             pszText = "Western Europe CDA, Lannion, France";
    1203               0 :             break;
    1204                 :         case SO:
    1205               0 :             pszText = "SOCC (Satellite Operations Control Center), Suitland, Maryland, USA";
    1206               0 :             break;
    1207                 :         case WI:
    1208               7 :             pszText = "Wallops Island, Virginia, USA";
    1209               7 :             break;
    1210                 :         default:
    1211               2 :             pszText = "Unknown receiving station";
    1212                 :             break;
    1213                 :     }
    1214              12 :     SetMetadataItem( "SOURCE",  pszText );
    1215                 : 
    1216              12 :     switch( eProcCenter )
    1217                 :     {
    1218                 :         case CMS:
    1219               0 :             pszText = "Centre de Meteorologie Spatiale - Lannion, France";
    1220               0 :             break;
    1221                 :         case DSS:
    1222               2 :             pszText = "Dundee Satellite Receiving Station - Dundee, Scotland, UK";
    1223               2 :             break;
    1224                 :         case NSS:
    1225              10 :             pszText = "NOAA/NESDIS - Suitland, Maryland, USA";
    1226              10 :             break;
    1227                 :         case UKM:
    1228               0 :             pszText = "United Kingdom Meteorological Office - Bracknell, England, UK";
    1229               0 :             break;
    1230                 :         default:
    1231               0 :             pszText = "Unknown processing center";
    1232                 :             break;
    1233                 :     }
    1234              12 :     SetMetadataItem( "PROCESSING_CENTER",  pszText );
    1235                 :     
    1236              12 :     return CE_None;
    1237                 : }
    1238                 : 
    1239                 : /************************************************************************/
    1240                 : /*                        ComputeFileOffsets()                          */
    1241                 : /************************************************************************/
    1242                 : 
    1243              16 : int L1BDataset::ComputeFileOffsets()
    1244                 : {
    1245              16 :     switch( eProductType )
    1246                 :     {
    1247                 :         case HRPT:
    1248                 :         case LAC:
    1249                 :         case FRAC:
    1250               6 :             nRasterXSize = 2048;
    1251               6 :             nBufferSize = 20484;
    1252               6 :             iGCPStart = 25;
    1253               6 :             iGCPStep = 40;
    1254               6 :             nGCPsPerLine = 51;
    1255               6 :             if ( eL1BFormat == L1B_NOAA9 )
    1256                 :             {
    1257               4 :                 if (iDataFormat == PACKED10BIT)
    1258                 :                 {
    1259               2 :                     nRecordSize = 14800;
    1260               2 :                     nRecordDataEnd = 14104;
    1261                 :                 }
    1262               2 :                 else if (iDataFormat == UNPACKED16BIT)
    1263                 :                 {
    1264               1 :                     switch(nBands)
    1265                 :                     {
    1266                 :                         case 1:
    1267               1 :                         nRecordSize = 4544;
    1268               1 :                         nRecordDataEnd = 4544;
    1269               1 :                         break;
    1270                 :                         case 2:
    1271               0 :                         nRecordSize = 8640;
    1272               0 :                         nRecordDataEnd = 8640;
    1273               0 :                         break;
    1274                 :                         case 3:
    1275               0 :                         nRecordSize = 12736;
    1276               0 :                         nRecordDataEnd = 12736;
    1277               0 :                         break;
    1278                 :                         case 4:
    1279               0 :                         nRecordSize = 16832;
    1280               0 :                         nRecordDataEnd = 16832;
    1281               0 :                         break;
    1282                 :                         case 5:
    1283               0 :                         nRecordSize = 20928;
    1284               0 :                         nRecordDataEnd = 20928;
    1285                 :                         break;
    1286                 :                     }
    1287                 :                 }
    1288                 :                 else // UNPACKED8BIT
    1289                 :                 {
    1290               1 :                     switch(nBands)
    1291                 :                     {
    1292                 :                         case 1:
    1293               1 :                         nRecordSize = 2496;
    1294               1 :                         nRecordDataEnd = 2496;
    1295               1 :                         break;
    1296                 :                         case 2:
    1297               0 :                         nRecordSize = 4544;
    1298               0 :                         nRecordDataEnd = 4544;
    1299               0 :                         break;
    1300                 :                         case 3:
    1301               0 :                         nRecordSize = 6592;
    1302               0 :                         nRecordDataEnd = 6592;
    1303               0 :                         break;
    1304                 :                         case 4:
    1305               0 :                         nRecordSize = 8640;
    1306               0 :                         nRecordDataEnd = 8640;
    1307               0 :                         break;
    1308                 :                         case 5:
    1309               0 :                         nRecordSize = 10688;
    1310               0 :                         nRecordDataEnd = 10688;
    1311                 :                         break;
    1312                 :                     }
    1313                 :                 }
    1314               4 :                 nDataStartOffset = nRecordSize + L1B_NOAA9_HEADER_SIZE;
    1315               4 :                 nRecordDataStart = 448;
    1316               4 :                 iGCPCodeOffset = 52;
    1317               4 :                 iGCPOffset = 104;
    1318                 :             }
    1319                 : 
    1320               4 :             else if ( eL1BFormat == L1B_NOAA15
    1321                 :                       || eL1BFormat == L1B_NOAA15_NOHDR )
    1322                 :             {
    1323               2 :                 if (iDataFormat == PACKED10BIT)
    1324                 :                 {
    1325               2 :                     nRecordSize = 15872;
    1326               2 :                     nRecordDataEnd = 14920;
    1327                 :                 }
    1328               0 :                 else if (iDataFormat == UNPACKED16BIT)
    1329                 :                 {
    1330               0 :                     switch(nBands)
    1331                 :                     {
    1332                 :                         case 1:
    1333               0 :                         nRecordSize = 6144;
    1334               0 :                         nRecordDataEnd = 5360;
    1335               0 :                         break;
    1336                 :                         case 2:
    1337               0 :                         nRecordSize = 10240;
    1338               0 :                         nRecordDataEnd = 9456;
    1339               0 :                         break;
    1340                 :                         case 3:
    1341               0 :                         nRecordSize = 14336;
    1342               0 :                         nRecordDataEnd = 13552;
    1343               0 :                         break;
    1344                 :                         case 4:
    1345               0 :                         nRecordSize = 18432;
    1346               0 :                         nRecordDataEnd = 17648;
    1347               0 :                         break;
    1348                 :                         case 5:
    1349               0 :                         nRecordSize = 22528;
    1350               0 :                         nRecordDataEnd = 21744;
    1351                 :                         break;
    1352                 :                     }
    1353                 :                 }
    1354                 :                 else // UNPACKED8BIT
    1355                 :                 {
    1356               0 :                     switch(nBands)
    1357                 :                     {
    1358                 :                         case 1:
    1359               0 :                         nRecordSize = 4096;
    1360               0 :                         nRecordDataEnd = 3312;
    1361               0 :                         break;
    1362                 :                         case 2:
    1363               0 :                         nRecordSize = 6144;
    1364               0 :                         nRecordDataEnd = 5360;
    1365               0 :                         break;
    1366                 :                         case 3:
    1367               0 :                         nRecordSize = 8192;
    1368               0 :                         nRecordDataEnd = 7408;
    1369               0 :                         break;
    1370                 :                         case 4:
    1371               0 :                         nRecordSize = 10240;
    1372               0 :                         nRecordDataEnd = 9456;
    1373               0 :                         break;
    1374                 :                         case 5:
    1375               0 :                         nRecordSize = 12288;
    1376               0 :                         nRecordDataEnd = 11504;
    1377                 :                         break;
    1378                 :                     }
    1379                 :                 }
    1380                 :                 nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ?
    1381               2 :                     nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
    1382               2 :                 nRecordDataStart = 1264;
    1383               2 :                 iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
    1384               2 :                 iGCPOffset = 640;
    1385                 :             }
    1386                 :             else
    1387               0 :                 return 0;
    1388               6 :             break;
    1389                 : 
    1390                 :         case GAC:
    1391              10 :             nRasterXSize = 409;
    1392              10 :             nBufferSize = 4092;
    1393              10 :             iGCPStart = 5; // FIXME: depends of scan direction
    1394              10 :             iGCPStep = 8;
    1395              10 :             nGCPsPerLine = 51;
    1396              10 :             if (  eL1BFormat == L1B_NOAA9 )
    1397                 :             {
    1398               7 :                 if (iDataFormat == PACKED10BIT)
    1399                 :                 {
    1400               3 :                     nRecordSize = 3220;
    1401               3 :                     nRecordDataEnd = 3176;
    1402                 :                 }
    1403               4 :                 else if (iDataFormat == UNPACKED16BIT)
    1404               2 :                     switch(nBands)
    1405                 :                     {
    1406                 :                         case 1:
    1407               0 :                         nRecordSize = 1268;
    1408               0 :                         nRecordDataEnd = 1266;
    1409               0 :                         break;
    1410                 :                         case 2:
    1411               1 :                         nRecordSize = 2084;
    1412               1 :                         nRecordDataEnd = 2084;
    1413               1 :                         break;
    1414                 :                         case 3:
    1415               0 :                         nRecordSize = 2904;
    1416               0 :                         nRecordDataEnd = 2902;
    1417               0 :                         break;
    1418                 :                         case 4:
    1419               0 :                         nRecordSize = 3720;
    1420               0 :                         nRecordDataEnd = 3720;
    1421               0 :                         break;
    1422                 :                         case 5:
    1423               1 :                         nRecordSize = 4540;
    1424               1 :                         nRecordDataEnd = 4538;
    1425                 :                         break;
    1426                 :                     }
    1427                 :                 else // UNPACKED8BIT
    1428                 :                 {
    1429               2 :                     switch(nBands)
    1430                 :                     {
    1431                 :                         case 1:
    1432               0 :                         nRecordSize = 860;
    1433               0 :                         nRecordDataEnd = 858;
    1434               0 :                         break;
    1435                 :                         case 2:
    1436               1 :                         nRecordSize = 1268;
    1437               1 :                         nRecordDataEnd = 1266;
    1438               1 :                         break;
    1439                 :                         case 3:
    1440               0 :                         nRecordSize = 1676;
    1441               0 :                         nRecordDataEnd = 1676;
    1442               0 :                         break;
    1443                 :                         case 4:
    1444               0 :                         nRecordSize = 2084;
    1445               0 :                         nRecordDataEnd = 2084;
    1446               0 :                         break;
    1447                 :                         case 5:
    1448               1 :                         nRecordSize = 2496;
    1449               1 :                         nRecordDataEnd = 2494;
    1450                 :                         break;
    1451                 :                     }
    1452                 :                 }
    1453               7 :                 nDataStartOffset = nRecordSize * 2 + L1B_NOAA9_HEADER_SIZE;
    1454               7 :                 nRecordDataStart = 448;
    1455               7 :                 iGCPCodeOffset = 52;
    1456               7 :                 iGCPOffset = 104;
    1457                 :             }
    1458                 : 
    1459               6 :             else if ( eL1BFormat == L1B_NOAA15
    1460                 :                       || eL1BFormat == L1B_NOAA15_NOHDR )
    1461                 :             {
    1462               3 :                 if (iDataFormat == PACKED10BIT)
    1463                 :                 {
    1464               1 :                     nRecordSize = 4608;
    1465               1 :                     nRecordDataEnd = 3992;
    1466                 :                 }
    1467               2 :                 else if (iDataFormat == UNPACKED16BIT)
    1468                 :                 {
    1469               1 :                     switch(nBands)
    1470                 :                     {
    1471                 :                         case 1:
    1472               0 :                         nRecordSize = 2360;
    1473               0 :                         nRecordDataEnd = 2082;
    1474               0 :                         break;
    1475                 :                         case 2:
    1476               0 :                         nRecordSize = 3176;
    1477               0 :                         nRecordDataEnd = 2900;
    1478               0 :                         break;
    1479                 :                         case 3:
    1480               0 :                         nRecordSize = 3992;
    1481               0 :                         nRecordDataEnd = 3718;
    1482               0 :                         break;
    1483                 :                         case 4:
    1484               0 :                         nRecordSize = 4816;
    1485               0 :                         nRecordDataEnd = 4536;
    1486               0 :                         break;
    1487                 :                         case 5:
    1488               1 :                         nRecordSize = 5632;
    1489               1 :                         nRecordDataEnd = 5354;
    1490                 :                         break;
    1491                 :                     }
    1492                 :                 }
    1493                 :                 else // UNPACKED8BIT
    1494                 :                 {
    1495               1 :                     switch(nBands)
    1496                 :                     {
    1497                 :                         case 1:
    1498               0 :                         nRecordSize = 1952;
    1499               0 :                         nRecordDataEnd = 1673;
    1500               0 :                         break;
    1501                 :                         case 2:
    1502               0 :                         nRecordSize = 2360;
    1503               0 :                         nRecordDataEnd = 2082;
    1504               0 :                         break;
    1505                 :                         case 3:
    1506               0 :                         nRecordSize = 2768;
    1507               0 :                         nRecordDataEnd = 2491;
    1508               0 :                         break;
    1509                 :                         case 4:
    1510               0 :                         nRecordSize = 3176;
    1511               0 :                         nRecordDataEnd = 2900;
    1512               0 :                         break;
    1513                 :                         case 5:
    1514               1 :                         nRecordSize = 3584;
    1515               1 :                         nRecordDataEnd = 3309;
    1516                 :                         break;
    1517                 :                     }
    1518                 :                 }
    1519                 :                 nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ?
    1520               3 :                     nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
    1521               3 :                 nRecordDataStart = 1264;
    1522               3 :                 iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
    1523               3 :                 iGCPOffset = 640;
    1524                 :             }
    1525                 :             else
    1526               0 :                 return 0;
    1527              10 :         break;
    1528                 :         default:
    1529               0 :             return 0;
    1530                 :     }
    1531                 : 
    1532              16 :     return 1;
    1533                 : }
    1534                 : 
    1535                 : /************************************************************************/
    1536                 : /*                           DetectFormat()                             */
    1537                 : /************************************************************************/
    1538                 : 
    1539            8959 : int L1BDataset::DetectFormat( GDALOpenInfo *poOpenInfo )
    1540                 : 
    1541                 : {
    1542            8959 :     GByte* pabyHeader = poOpenInfo->pabyHeader;
    1543            8959 :     if (pabyHeader == NULL || poOpenInfo->nHeaderBytes < L1B_NOAA9_HEADER_SIZE)
    1544            8471 :         return L1B_NONE;
    1545                 : 
    1546                 :     // We will try the NOAA-15 and later formats first
    1547             488 :     if ( poOpenInfo->nHeaderBytes > L1B_NOAA15_HEADER_SIZE + 61
    1548                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 25) == '.'
    1549                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 30) == '.'
    1550                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 33) == '.'
    1551                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 40) == '.'
    1552                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 46) == '.'
    1553                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 52) == '.'
    1554                 :          && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 61) == '.' )
    1555               5 :         return L1B_NOAA15;
    1556                 : 
    1557                 :     // Next try the NOAA-9/14 formats
    1558             483 :     if ( *(pabyHeader + 8 + 25) == '.'
    1559                 :          && *(pabyHeader + 8 + 30) == '.'
    1560                 :          && *(pabyHeader + 8 + 33) == '.'
    1561                 :          && *(pabyHeader + 8 + 40) == '.'
    1562                 :          && *(pabyHeader + 8 + 46) == '.'
    1563                 :          && *(pabyHeader + 8 + 52) == '.'
    1564                 :          && *(pabyHeader + 8 + 61) == '.' )
    1565               7 :         return L1B_NOAA9;
    1566                 : 
    1567                 :     // Finally try the AAPP formats 
    1568             476 :     if ( *(pabyHeader + 25) == '.'
    1569                 :          && *(pabyHeader + 30) == '.'
    1570                 :          && *(pabyHeader + 33) == '.'
    1571                 :          && *(pabyHeader + 40) == '.'
    1572                 :          && *(pabyHeader + 46) == '.'
    1573                 :          && *(pabyHeader + 52) == '.'
    1574                 :          && *(pabyHeader + 61) == '.' )
    1575               0 :         return L1B_NOAA15_NOHDR;
    1576                 : 
    1577             476 :     return L1B_NONE;
    1578                 : }
    1579                 : 
    1580                 : /************************************************************************/
    1581                 : /*                              Identify()                              */
    1582                 : /************************************************************************/
    1583                 : 
    1584               0 : int L1BDataset::Identify( GDALOpenInfo *poOpenInfo )
    1585                 : 
    1586                 : {
    1587               0 :     if( poOpenInfo->fp == NULL )
    1588               0 :         return FALSE;
    1589                 : 
    1590               0 :     if ( DetectFormat(poOpenInfo) == L1B_NONE )
    1591               0 :         return FALSE;
    1592                 : 
    1593               0 :     return TRUE;
    1594                 : }
    1595                 : 
    1596                 : /************************************************************************/
    1597                 : /*                                Open()                                */
    1598                 : /************************************************************************/
    1599                 : 
    1600            8959 : GDALDataset *L1BDataset::Open( GDALOpenInfo * poOpenInfo )
    1601                 : 
    1602                 : {
    1603            8959 :     int     eL1BFormat = DetectFormat( poOpenInfo );
    1604            8959 :     if ( eL1BFormat == L1B_NONE )
    1605            8947 :         return NULL;
    1606                 :         
    1607                 : /* -------------------------------------------------------------------- */
    1608                 : /*      Confirm the requested access is supported.                      */
    1609                 : /* -------------------------------------------------------------------- */
    1610              12 :     if( poOpenInfo->eAccess == GA_Update )
    1611                 :     {
    1612                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1613                 :                   "The L1B driver does not support update access to existing"
    1614               0 :                   " datasets.\n" );
    1615               0 :         return NULL;
    1616                 :     }
    1617                 :     
    1618                 : #if 0
    1619                 :     Geolocation
    1620                 :     if ( EQUAL( poOpenInfo->pszFilename, "L1BGCPS:" ) )
    1621                 :         bFetchGeolocation = TRUE;
    1622                 : #endif
    1623                 : 
    1624                 : /* -------------------------------------------------------------------- */
    1625                 : /*      Create a corresponding GDALDataset.                             */
    1626                 : /* -------------------------------------------------------------------- */
    1627                 :     L1BDataset  *poDS;
    1628                 :     VSIStatBuf  sStat;
    1629              12 :     const char  *pszFilename = poOpenInfo->pszFilename;
    1630                 : 
    1631              12 :     poDS = new L1BDataset( eL1BFormat );
    1632                 : 
    1633              12 :     poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
    1634              12 :     if ( !poDS->fp )
    1635                 :     {
    1636               0 :         CPLDebug( "L1B", "Can't open file \"%s\".", poOpenInfo->pszFilename );
    1637               0 :         goto bad;
    1638                 :     }
    1639                 :     
    1640                 : /* -------------------------------------------------------------------- */
    1641                 : /*      Read the header.                                                */
    1642                 : /* -------------------------------------------------------------------- */
    1643              12 :     if ( poDS->ProcessDatasetHeader() != CE_None )
    1644                 :     {
    1645               0 :         CPLDebug( "L1B", "Error reading L1B record header." );
    1646               0 :         goto bad;
    1647                 :     }
    1648                 : 
    1649              12 :     CPLStat(pszFilename, &sStat);
    1650                 : 
    1651              12 :     if ( poDS->bGuessDataFormat )
    1652                 :     {
    1653                 :         int nTempYSize;
    1654                 :         GUInt16 nScanlineNumber;
    1655                 :         int j;
    1656                 : 
    1657                 :         /* If the data format is unspecified, try each one of the 3 known data formats */
    1658                 :         /* It is considered valid when the spacing between the first 5 scanline numbers */
    1659                 :         /* is a constant */
    1660                 : 
    1661               8 :         for(j=0;j<3;j++)
    1662                 :         {
    1663               8 :             poDS->iDataFormat = PACKED10BIT + j;
    1664               8 :             if (!poDS->ComputeFileOffsets())
    1665               0 :                 goto bad;
    1666                 : 
    1667               8 :             nTempYSize = (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
    1668               8 :             if (nTempYSize < 5)
    1669               1 :                 continue;
    1670                 : 
    1671               7 :             int nLastScanlineNumber = 0;
    1672               7 :             int nDiffLine = 0;
    1673                 :             int i;
    1674              33 :             for (i=0;i<5;i++)
    1675                 :             {
    1676              29 :                 nScanlineNumber = 0;
    1677                 : 
    1678              29 :                 VSIFSeekL(poDS->fp, poDS->nDataStartOffset + i * poDS->nRecordSize, SEEK_SET);
    1679              29 :                 VSIFReadL(&nScanlineNumber, 1, 2, poDS->fp);
    1680                 : #ifdef CPL_LSB
    1681              29 :                 CPL_SWAP16PTR( &nScanlineNumber );
    1682                 : #endif
    1683              29 :                 if (i == 1)
    1684                 :                 {
    1685               7 :                     nDiffLine = nScanlineNumber - nLastScanlineNumber;
    1686               7 :                     if (nDiffLine == 0)
    1687               0 :                         break;
    1688                 :                 }
    1689              22 :                 else if (i > 1)
    1690                 :                 {
    1691              15 :                     if (nDiffLine != nScanlineNumber - nLastScanlineNumber)
    1692               3 :                         break;
    1693                 :                 }
    1694                 : 
    1695              26 :                 nLastScanlineNumber = nScanlineNumber;
    1696                 :             }
    1697                 : 
    1698               7 :             if (i == 5)
    1699                 :             {
    1700                 :                 CPLDebug("L1B", "Guessed data format : %s",
    1701                 :                          (poDS->iDataFormat == PACKED10BIT) ? "10" :
    1702               4 :                          (poDS->iDataFormat == UNPACKED8BIT) ? "08" : "16");
    1703               4 :                 break;
    1704                 :             }
    1705                 :         }
    1706                 : 
    1707               4 :         if (j == 3)
    1708                 :         {
    1709               0 :             CPLError(CE_Failure, CPLE_AppDefined, "Could not guess data format of L1B product");
    1710               0 :             goto bad;
    1711                 :         }
    1712                 :     }
    1713                 :     else
    1714                 :     {
    1715               8 :         if (!poDS->ComputeFileOffsets())
    1716               0 :             goto bad;
    1717                 :     }
    1718                 : 
    1719                 :     // Compute number of lines dinamycally, so we can read partially
    1720                 :     // downloaded files
    1721                 :     poDS->nRasterYSize =
    1722              12 :         (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
    1723                 : 
    1724                 : /* -------------------------------------------------------------------- */
    1725                 : /*      Create band information objects.                                */
    1726                 : /* -------------------------------------------------------------------- */
    1727                 :     int iBand, i;
    1728                 :     
    1729              65 :     for( iBand = 1, i = 0; iBand <= poDS->nBands; iBand++ )
    1730                 :     {
    1731              53 :         poDS->SetBand( iBand, new L1BRasterBand( poDS, iBand ));
    1732                 :         
    1733                 :         // Channels descriptions
    1734             106 :         if ( poDS->eSpacecraftID >= NOAA6 && poDS->eSpacecraftID <= METOP2 )
    1735                 :         {
    1736              53 :             if ( !(i & 0x01) && poDS->iChannelsMask & 0x01 )
    1737                 :             {
    1738              10 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[0] );
    1739              10 :                 i |= 0x01;
    1740              10 :                 continue;
    1741                 :             }
    1742              43 :             if ( !(i & 0x02) && poDS->iChannelsMask & 0x02 )
    1743                 :             {
    1744              12 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[1] );
    1745              12 :                 i |= 0x02;
    1746              12 :                 continue;
    1747                 :             }
    1748              31 :             if ( !(i & 0x04) && poDS->iChannelsMask & 0x04 )
    1749                 :             {
    1750              15 :                 if ( poDS->eSpacecraftID >= NOAA15
    1751                 :                      && poDS->eSpacecraftID <= METOP2 )
    1752               5 :                     if ( poDS->iInstrumentStatus & 0x0400 )
    1753               5 :                         poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[7] );
    1754                 :                     else
    1755               0 :                         poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[6] );
    1756                 :                 else    
    1757               5 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[2] );
    1758              10 :                 i |= 0x04;
    1759              10 :                 continue;
    1760                 :             }
    1761              21 :             if ( !(i & 0x08) && poDS->iChannelsMask & 0x08 )
    1762                 :             {
    1763              11 :                 poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] );
    1764              11 :                 i |= 0x08;
    1765              11 :                 continue;
    1766                 :             }
    1767              10 :             if ( !(i & 0x10) && poDS->iChannelsMask & 0x10 )
    1768                 :             {
    1769              10 :                 if (poDS->eSpacecraftID == NOAA13)              // 5 NOAA-13
    1770               0 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[5] );
    1771              10 :                 else if (poDS->eSpacecraftID == NOAA6 ||
    1772                 :                          poDS->eSpacecraftID == NOAA8 ||
    1773                 :                          poDS->eSpacecraftID == NOAA10)         // 4 NOAA-6,-8,-10
    1774               0 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] );
    1775                 :                 else
    1776              10 :                     poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[4] );
    1777              10 :                 i |= 0x10;
    1778              10 :                 continue;
    1779                 :             }
    1780                 :         }
    1781                 :     }
    1782                 : 
    1783                 : /* -------------------------------------------------------------------- */
    1784                 : /*      Do we have GCPs?                                                */
    1785                 : /* -------------------------------------------------------------------- */
    1786                 :     if ( 1/*EQUALN((const char *)pabyTBMHeader + 96, "Y", 1)*/ )
    1787                 :     {
    1788              12 :         poDS->ProcessRecordHeaders();
    1789                 : 
    1790                 : #if 0
    1791                 :     Geolocation
    1792                 :         CPLString  osTMP;
    1793                 : 
    1794                 :         poDS->SetMetadataItem( "SRS", poDS->pszGCPProjection, "GEOLOCATION" );
    1795                 :         
    1796                 :         osTMP.Printf( "L1BGCPS:\"%s\"", pszFilename );
    1797                 :         poDS->SetMetadataItem( "X_DATASET", osTMP, "GEOLOCATION" );
    1798                 :         poDS->SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" );
    1799                 :         poDS->SetMetadataItem( "Y_DATASET", osTMP, "GEOLOCATION" );
    1800                 :         poDS->SetMetadataItem( "Y_BAND", "2" , "GEOLOCATION" );
    1801                 : 
    1802                 :         osTMP.Printf( "%d", (poDS->eLocationIndicator == DESCEND) ?
    1803                 :             poDS->iGCPStart : (poDS->nRasterXSize - poDS->iGCPStart) );
    1804                 :         poDS->SetMetadataItem( "PIXEL_OFFSET", osTMP, "GEOLOCATION" );
    1805                 :         osTMP.Printf( "%d", (poDS->eLocationIndicator == DESCEND) ?
    1806                 :                       poDS->iGCPStep : -poDS->iGCPStep );
    1807                 :         poDS->SetMetadataItem( "PIXEL_STEP", osTMP, "GEOLOCATION" );
    1808                 : 
    1809                 :         poDS->SetMetadataItem( "LINE_OFFSET", "0", "GEOLOCATION" );
    1810                 :         poDS->SetMetadataItem( "LINE_STEP", "1", "GEOLOCATION" );
    1811                 : #endif
    1812                 :     }
    1813                 : 
    1814                 : /* -------------------------------------------------------------------- */
    1815                 : /*      Initialize any PAM information.                                 */
    1816                 : /* -------------------------------------------------------------------- */
    1817              12 :     poDS->SetDescription( poOpenInfo->pszFilename );
    1818              12 :     poDS->TryLoadXML();
    1819                 : 
    1820              12 :     return( poDS );
    1821                 : 
    1822                 : bad:
    1823               0 :     delete poDS;
    1824               0 :     return NULL;
    1825                 : }
    1826                 : 
    1827                 : /************************************************************************/
    1828                 : /*                        GDALRegister_L1B()                            */
    1829                 : /************************************************************************/
    1830                 : 
    1831             338 : void GDALRegister_L1B()
    1832                 : 
    1833                 : {
    1834                 :     GDALDriver  *poDriver;
    1835                 : 
    1836             338 :     if( GDALGetDriverByName( "L1B" ) == NULL )
    1837                 :     {
    1838             336 :         poDriver = new GDALDriver();
    1839                 :         
    1840             336 :         poDriver->SetDescription( "L1B" );
    1841                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
    1842             336 :                                    "NOAA Polar Orbiter Level 1b Data Set" );
    1843                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
    1844             336 :                                    "frmt_l1b.html" );
    1845                 : 
    1846             336 :         poDriver->pfnOpen = L1BDataset::Open;
    1847                 : 
    1848             336 :         GetGDALDriverManager()->RegisterDriver( poDriver );
    1849                 :     }
    1850             338 : }
    1851                 : 

Generated by: LCOV version 1.7