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