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

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

Generated by: LCOV version 1.7