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 :
|