1 : /******************************************************************************
2 : * $Id: l1bdataset.cpp 24093 2012-03-07 20:43:20Z 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 24093 2012-03-07 20:43:20Z 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 48 : void SetYear(long year)
185 : {
186 48 : lYear = year;
187 48 : }
188 48 : void SetDay(long day)
189 : {
190 48 : lDay = day;
191 48 : }
192 48 : void SetMillisecond(long millisecond)
193 : {
194 48 : lMillisecond = millisecond;
195 48 : }
196 48 : char* PrintTime()
197 : {
198 : snprintf(pszString, L1B_TIMECODE_LENGTH,
199 : "year: %ld, day: %ld, millisecond: %ld",
200 48 : lYear, lDay, lMillisecond);
201 48 : 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 106 : {
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 106 : L1BRasterBand::L1BRasterBand( L1BDataset *poDS, int nBand )
294 :
295 : {
296 106 : this->poDS = poDS;
297 106 : this->nBand = nBand;
298 106 : eDataType = GDT_UInt16;
299 :
300 106 : nBlockXSize = poDS->GetRasterXSize();
301 106 : nBlockYSize = 1;
302 106 : }
303 :
304 : /************************************************************************/
305 : /* IReadBlock() */
306 : /************************************************************************/
307 :
308 560 : CPLErr L1BRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
309 : void * pImage )
310 : {
311 560 : 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 560 : (nRasterYSize - nBlockYOff - 1) * poGDS->nRecordSize;
320 560 : VSIFSeekL( poGDS->fp, iDataOffset, SEEK_SET );
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Read data into the buffer. */
324 : /* -------------------------------------------------------------------- */
325 560 : GUInt16 *iScan = NULL; // Unpacked 16-bit scanline buffer
326 : int i, j;
327 :
328 560 : switch (poGDS->iDataFormat)
329 : {
330 : case PACKED10BIT:
331 : {
332 : // Read packed scanline
333 204 : GUInt32 *iRawScan = (GUInt32 *)CPLMalloc(poGDS->nRecordSize);
334 204 : VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp );
335 :
336 204 : iScan = (GUInt16 *)CPLMalloc(poGDS->nBufferSize);
337 204 : j = 0;
338 221292 : for(i = poGDS->nRecordDataStart / (int)sizeof(iRawScan[0]);
339 : i < poGDS->nRecordDataEnd / (int)sizeof(iRawScan[0]); i++)
340 : {
341 221088 : GUInt32 iWord1 = CPL_MSBWORD32( iRawScan[i] );
342 221088 : GUInt32 iWord2 = iWord1 & 0x3FF00000;
343 :
344 221088 : iScan[j++] = (GUInt16) (iWord2 >> 20);
345 221088 : iWord2 = iWord1 & 0x000FFC00;
346 221088 : iScan[j++] = (GUInt16) (iWord2 >> 10);
347 221088 : iScan[j++] = (GUInt16) (iWord1 & 0x000003FF);
348 : }
349 204 : CPLFree(iRawScan);
350 : }
351 204 : break;
352 : case UNPACKED16BIT:
353 : {
354 : // Read unpacked scanline
355 198 : GUInt16 *iRawScan = (GUInt16 *)CPLMalloc(poGDS->nRecordSize);
356 198 : VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp );
357 :
358 : iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
359 198 : * poGDS->nBands * sizeof(GUInt16));
360 375690 : for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
361 : {
362 750984 : iScan[i] = CPL_MSBWORD16( iRawScan[poGDS->nRecordDataStart
363 750984 : / (int)sizeof(iRawScan[0]) + i] );
364 : }
365 198 : CPLFree(iRawScan);
366 : }
367 198 : break;
368 : case UNPACKED8BIT:
369 : {
370 : // Read 8-bit unpacked scanline
371 158 : GByte *byRawScan = (GByte *)CPLMalloc(poGDS->nRecordSize);
372 158 : VSIFReadL( byRawScan, 1, poGDS->nRecordSize, poGDS->fp );
373 :
374 : iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
375 158 : * poGDS->nBands * sizeof(GUInt16));
376 323268 : for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
377 323110 : iScan[i] = byRawScan[poGDS->nRecordDataStart
378 323110 : / (int)sizeof(byRawScan[0]) + i];
379 158 : CPLFree(byRawScan);
380 : }
381 : break;
382 : default: // NOTREACHED
383 : break;
384 : }
385 :
386 560 : int nBlockSize = nBlockXSize * nBlockYSize;
387 560 : if (poGDS->eLocationIndicator == DESCEND)
388 : {
389 223014 : for( i = 0, j = 0; i < nBlockSize; i++ )
390 : {
391 222574 : ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1];
392 222574 : j += poGDS->nBands;
393 : }
394 : }
395 : else
396 : {
397 72146 : for ( i = nBlockSize - 1, j = 0; i >= 0; i-- )
398 : {
399 72026 : ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1];
400 72026 : j += poGDS->nBands;
401 : }
402 : }
403 :
404 560 : CPLFree(iScan);
405 560 : return CE_None;
406 : }
407 :
408 : /************************************************************************/
409 : /* L1BDataset() */
410 : /************************************************************************/
411 :
412 24 : L1BDataset::L1BDataset( int eL1BFormat )
413 :
414 : {
415 24 : this->eL1BFormat = eL1BFormat;
416 24 : fp = NULL;
417 24 : nGCPCount = 0;
418 24 : pasGCPList = NULL;
419 24 : 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 24 : nBands = 0;
421 24 : eLocationIndicator = DESCEND; // XXX: should be initialised
422 24 : iChannelsMask = 0;
423 24 : iInstrumentStatus = 0;
424 24 : bFetchGeolocation = FALSE;
425 24 : bGuessDataFormat = FALSE;
426 24 : }
427 :
428 : /************************************************************************/
429 : /* ~L1BDataset() */
430 : /************************************************************************/
431 :
432 24 : L1BDataset::~L1BDataset()
433 :
434 : {
435 24 : FlushCache();
436 :
437 24 : if( nGCPCount > 0 )
438 : {
439 24 : GDALDeinitGCPs( nGCPCount, pasGCPList );
440 24 : CPLFree( pasGCPList );
441 : }
442 24 : if ( pszGCPProjection )
443 24 : CPLFree( pszGCPProjection );
444 24 : if( fp != NULL )
445 24 : VSIFCloseL( fp );
446 24 : }
447 :
448 : /************************************************************************/
449 : /* GetGCPCount() */
450 : /************************************************************************/
451 :
452 24 : int L1BDataset::GetGCPCount()
453 :
454 : {
455 24 : 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 24 : const GDAL_GCP *L1BDataset::GetGCPs()
476 : {
477 24 : return pasGCPList;
478 : }
479 :
480 : /************************************************************************/
481 : /* Fetch timecode from the record header (NOAA9-NOAA14 version) */
482 : /************************************************************************/
483 :
484 28 : void L1BDataset::FetchNOAA9TimeCode( TimeCode *psTime, GByte *piRecordHeader,
485 : int *peLocationIndicator )
486 : {
487 : GUInt32 lTemp;
488 :
489 28 : lTemp = ((piRecordHeader[2] >> 1) & 0x7F);
490 : psTime->SetYear((lTemp > 77) ?
491 28 : (lTemp + 1900) : (lTemp + 2000)); // Avoid `Year 2000' problem
492 28 : psTime->SetDay((GUInt32)(piRecordHeader[2] & 0x01) << 8
493 28 : | (GUInt32)piRecordHeader[3]);
494 28 : psTime->SetMillisecond( ((GUInt32)(piRecordHeader[4] & 0x07) << 24)
495 28 : | ((GUInt32)piRecordHeader[5] << 16)
496 28 : | ((GUInt32)piRecordHeader[6] << 8)
497 84 : | (GUInt32)piRecordHeader[7] );
498 28 : if ( peLocationIndicator )
499 : {
500 : *peLocationIndicator =
501 14 : ((piRecordHeader[8] & 0x02) == 0) ? ASCEND : DESCEND;
502 : }
503 28 : }
504 :
505 : /************************************************************************/
506 : /* Fetch timecode from the record header (NOAA15-METOP2 version) */
507 : /************************************************************************/
508 :
509 20 : void L1BDataset::FetchNOAA15TimeCode( TimeCode *psTime,
510 : GUInt16 *piRecordHeader,
511 : int *peLocationIndicator )
512 : {
513 : #ifdef CPL_LSB
514 : GUInt16 iTemp;
515 : GUInt32 lTemp;
516 :
517 20 : iTemp = piRecordHeader[1];
518 20 : psTime->SetYear(CPL_SWAP16(iTemp));
519 20 : iTemp = piRecordHeader[2];
520 20 : psTime->SetDay(CPL_SWAP16(iTemp));
521 20 : lTemp = (GUInt32)CPL_SWAP16(piRecordHeader[4]) << 16 |
522 40 : (GUInt32)CPL_SWAP16(piRecordHeader[5]);
523 20 : psTime->SetMillisecond(lTemp);
524 20 : if ( peLocationIndicator )
525 : {
526 : // FIXME: hemisphere
527 : *peLocationIndicator =
528 10 : ((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 20 : }
542 :
543 : /************************************************************************/
544 : /* Fetch GCPs from the individual scanlines */
545 : /************************************************************************/
546 :
547 480 : 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 480 : double dfDelta = (eProductType == GAC) ? 0.9 : 0.5;
553 : double dfPixel = (eLocationIndicator == DESCEND) ?
554 480 : iGCPStart + dfDelta : (nRasterXSize - (iGCPStart + dfDelta));
555 :
556 : int nGCPs;
557 480 : 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 280 : *(pabyRecordHeader + iGCPCodeOffset) : nGCPsPerLine;
563 : #ifdef DEBUG
564 : CPLDebug( "L1B", "iGCPCodeOffset=%d, nGCPsPerLine=%d, nGoodGCPs=%d",
565 280 : iGCPCodeOffset, nGCPsPerLine, nGCPs );
566 : #endif
567 : }
568 : else
569 200 : nGCPs = nGCPsPerLine;
570 :
571 480 : pabyRecordHeader += iGCPOffset;
572 :
573 25440 : while ( nGCPs-- )
574 : {
575 24480 : if ( eSpacecraftID <= NOAA14 )
576 : {
577 14280 : GInt16 nRawY = CPL_MSBWORD16( *(GInt16*)pabyRecordHeader );
578 14280 : pabyRecordHeader += sizeof(GInt16);
579 14280 : GInt16 nRawX = CPL_MSBWORD16( *(GInt16*)pabyRecordHeader );
580 14280 : pabyRecordHeader += sizeof(GInt16);
581 :
582 14280 : pasGCPList[nGCPCount].dfGCPY = nRawY / L1B_NOAA9_GCP_SCALE;
583 14280 : pasGCPList[nGCPCount].dfGCPX = nRawX / L1B_NOAA9_GCP_SCALE;
584 : }
585 : else
586 : {
587 10200 : GInt32 nRawY = CPL_MSBWORD32( *(GInt32*)pabyRecordHeader );
588 10200 : pabyRecordHeader += sizeof(GInt32);
589 10200 : GInt32 nRawX = CPL_MSBWORD32( *(GInt32*)pabyRecordHeader );
590 10200 : pabyRecordHeader += sizeof(GInt32);
591 :
592 10200 : pasGCPList[nGCPCount].dfGCPY = nRawY / L1B_NOAA15_GCP_SCALE;
593 10200 : pasGCPList[nGCPCount].dfGCPX = nRawX / L1B_NOAA15_GCP_SCALE;
594 : }
595 :
596 97920 : if ( pasGCPList[nGCPCount].dfGCPX < -180
597 24480 : || pasGCPList[nGCPCount].dfGCPX > 180
598 24480 : || pasGCPList[nGCPCount].dfGCPY < -90
599 24480 : || pasGCPList[nGCPCount].dfGCPY > 90 )
600 0 : continue;
601 :
602 24480 : pasGCPList[nGCPCount].dfGCPZ = 0.0;
603 24480 : pasGCPList[nGCPCount].dfGCPPixel = dfPixel;
604 24480 : dfPixel += (eLocationIndicator == DESCEND) ? iGCPStep : -iGCPStep;
605 24480 : pasGCPList[nGCPCount].dfGCPLine =
606 : (double)( (eLocationIndicator == DESCEND) ?
607 24480 : iLine : nRasterYSize - iLine - 1 ) + 0.5;
608 24480 : nGCPCount++;
609 : }
610 480 : }
611 :
612 : /************************************************************************/
613 : /* ProcessRecordHeaders() */
614 : /************************************************************************/
615 :
616 24 : void L1BDataset::ProcessRecordHeaders()
617 : {
618 24 : void *pRecordHeader = CPLMalloc( nRecordDataStart );
619 :
620 24 : VSIFSeekL(fp, nDataStartOffset, SEEK_SET);
621 24 : VSIFReadL(pRecordHeader, 1, nRecordDataStart, fp);
622 :
623 24 : if (eSpacecraftID <= NOAA14)
624 : {
625 : FetchNOAA9TimeCode( &sStartTime, (GByte *) pRecordHeader,
626 14 : &eLocationIndicator );
627 : }
628 : else
629 : {
630 : FetchNOAA15TimeCode( &sStartTime, (GUInt16 *) pRecordHeader,
631 10 : &eLocationIndicator );
632 : }
633 :
634 : VSIFSeekL( fp, nDataStartOffset + (nRasterYSize - 1) * nRecordSize,
635 24 : SEEK_SET);
636 24 : VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp );
637 :
638 24 : if (eSpacecraftID <= NOAA14)
639 14 : FetchNOAA9TimeCode( &sStopTime, (GByte *) pRecordHeader, NULL );
640 : else
641 10 : 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 24 : int nTargetLines = DESIRED_LINES_OF_GCPS;
649 24 : int nLineSkip = nRasterYSize / ( nTargetLines - 1 );
650 :
651 : /* -------------------------------------------------------------------- */
652 : /* Initialize the GCP list. */
653 : /* -------------------------------------------------------------------- */
654 : pasGCPList = (GDAL_GCP *)VSICalloc( nTargetLines * nGCPsPerLine,
655 24 : sizeof(GDAL_GCP) );
656 24 : if (pasGCPList == NULL)
657 : {
658 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
659 0 : CPLFree( pRecordHeader );
660 0 : return;
661 : }
662 24 : 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 504 : for( iStep = 0; iStep < nTargetLines; iStep++ )
672 : {
673 480 : int nOrigGCPs = nGCPCount;
674 : int iLine;
675 :
676 480 : if( iStep == nTargetLines - 1 )
677 24 : iLine = nRasterXSize - 1;
678 : else
679 456 : iLine = nLineSkip * iStep;
680 :
681 480 : VSIFSeekL( fp, nDataStartOffset + iLine * nRecordSize, SEEK_SET );
682 480 : VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp );
683 :
684 480 : 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 480 : int nGCPsOnThisLine = nGCPCount - nOrigGCPs;
692 480 : int nDesiredGCPsPerLine = MIN(DESIRED_GCPS_PER_LINE,nGCPsOnThisLine);
693 : int nGCPStep = ( nDesiredGCPsPerLine > 1 ) ?
694 480 : ( nGCPsOnThisLine - 1 ) / ( nDesiredGCPsPerLine-1 ) : 1;
695 480 : int iSrcGCP = nOrigGCPs;
696 480 : int iDstGCP = nOrigGCPs;
697 :
698 480 : if( nGCPStep == 0 )
699 0 : nGCPStep = 1;
700 :
701 5760 : for( iGCP = 0; iGCP < nDesiredGCPsPerLine; iGCP++ )
702 : {
703 5280 : iSrcGCP += iGCP * nGCPStep;
704 5280 : iDstGCP += iGCP;
705 :
706 5280 : pasGCPList[iDstGCP].dfGCPX = pasGCPList[iSrcGCP].dfGCPX;
707 5280 : pasGCPList[iDstGCP].dfGCPY = pasGCPList[iSrcGCP].dfGCPY;
708 5280 : pasGCPList[iDstGCP].dfGCPPixel = pasGCPList[iSrcGCP].dfGCPPixel;
709 5280 : pasGCPList[iDstGCP].dfGCPLine = pasGCPList[iSrcGCP].dfGCPLine;
710 : }
711 :
712 480 : nGCPCount = nOrigGCPs + nDesiredGCPsPerLine;
713 : }
714 :
715 24 : if( nGCPCount < nTargetLines * nGCPsPerLine )
716 : {
717 : GDALDeinitGCPs( nTargetLines * nGCPsPerLine - nGCPCount,
718 24 : pasGCPList + nGCPCount );
719 : }
720 :
721 24 : CPLFree( pRecordHeader );
722 :
723 : /* -------------------------------------------------------------------- */
724 : /* Set fetched information as metadata records */
725 : /* -------------------------------------------------------------------- */
726 : // Time of first scanline
727 24 : SetMetadataItem( "START", sStartTime.PrintTime() );
728 : // Time of last scanline
729 24 : SetMetadataItem( "STOP", sStopTime.PrintTime() );
730 : // AVHRR Earth location indication
731 :
732 24 : switch( eLocationIndicator )
733 : {
734 : case ASCEND:
735 8 : SetMetadataItem( "LOCATION", "Ascending" );
736 8 : break;
737 : case DESCEND:
738 : default:
739 16 : SetMetadataItem( "LOCATION", "Descending" );
740 : break;
741 : }
742 :
743 : }
744 :
745 : /************************************************************************/
746 : /* ProcessDatasetHeader() */
747 : /************************************************************************/
748 :
749 24 : CPLErr L1BDataset::ProcessDatasetHeader()
750 : {
751 : char szDatasetName[L1B_DATASET_NAME_SIZE + 1];
752 :
753 24 : if ( eL1BFormat == L1B_NOAA9 )
754 : {
755 : GByte abyTBMHeader[L1B_NOAA9_HEADER_SIZE];
756 :
757 14 : 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 14 : L1B_DATASET_NAME_SIZE );
769 14 : szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
770 :
771 : // Determine processing center where the dataset was created
772 14 : if ( EQUALN((const char *)abyTBMHeader
773 : + L1B_NOAA9_HDR_NAME_OFF, "CMS", 3) )
774 0 : eProcCenter = CMS;
775 14 : else if ( EQUALN((const char *)abyTBMHeader
776 : + L1B_NOAA9_HDR_NAME_OFF, "DSS", 3) )
777 0 : eProcCenter = DSS;
778 14 : else if ( EQUALN((const char *)abyTBMHeader
779 : + L1B_NOAA9_HDR_NAME_OFF, "NSS", 3) )
780 14 : 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 294 : for ( i = 0; i < L1B_NOAA9_HDR_CHAN_SIZE; i++ )
790 : {
791 534 : if ( abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 1
792 254 : || abyTBMHeader[L1B_NOAA9_HDR_CHAN_OFF + i] == 'Y' )
793 : {
794 26 : nBands++;
795 26 : iChannelsMask |= (1 << i);
796 : }
797 : }
798 14 : if ( nBands == 0 || nBands > 5 )
799 : {
800 6 : nBands = 5;
801 6 : iChannelsMask = 0x1F;
802 : }
803 :
804 : // Determine data format (10-bit packed or 8/16-bit unpacked)
805 14 : if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
806 : "10", 2) )
807 2 : iDataFormat = PACKED10BIT;
808 12 : else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
809 : "16", 2) )
810 2 : iDataFormat = UNPACKED16BIT;
811 10 : else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
812 : "08", 2) )
813 2 : iDataFormat = UNPACKED8BIT;
814 24 : else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF,
815 : " ", 2)
816 8 : || 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 8 : 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 14 : 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 14 : 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 8 : eSpacecraftID = NOAA12;
872 8 : break;
873 : case 2:
874 0 : eSpacecraftID = NOAA13;
875 0 : break;
876 : case 3:
877 6 : eSpacecraftID = NOAA14;
878 6 : 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 14 : int iWord = abyRecHeader[L1B_NOAA9_HDR_REC_PROD_OFF] >> 4;
889 14 : switch ( iWord )
890 : {
891 : case 1:
892 2 : eProductType = LAC;
893 2 : break;
894 : case 2:
895 10 : eProductType = GAC;
896 10 : break;
897 : case 3:
898 2 : eProductType = HRPT;
899 2 : 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 14 : iWord = ( abyRecHeader[L1B_NOAA9_HDR_REC_DSTAT_OFF] & 0x60 ) >> 5;
909 14 : switch( iWord )
910 : {
911 : case 1:
912 2 : eSource = GC;
913 2 : break;
914 : case 2:
915 12 : eSource = WI;
916 12 : break;
917 : case 3:
918 0 : eSource = SO;
919 0 : break;
920 : default:
921 0 : eSource = UNKNOWN_STATION;
922 : break;
923 : }
924 : }
925 :
926 20 : else if ( eL1BFormat == L1B_NOAA15 || eL1BFormat == L1B_NOAA15_NOHDR )
927 : {
928 10 : if ( eL1BFormat == L1B_NOAA15 )
929 : {
930 : GByte abyARSHeader[L1B_NOAA15_HEADER_SIZE];
931 :
932 10 : 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 210 : for ( i = 0; i < L1B_NOAA15_HDR_CHAN_SIZE; i++ )
943 : {
944 400 : if ( abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 1
945 200 : || abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 'Y' )
946 : {
947 140 : nBands++;
948 140 : iChannelsMask |= (1 << i);
949 : }
950 : }
951 10 : if ( nBands == 0 || nBands > 5 )
952 : {
953 6 : nBands = 5;
954 6 : iChannelsMask = 0x1F;
955 : }
956 :
957 : // Determine data format (10-bit packed or 8/16-bit unpacked)
958 10 : if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
959 : "10", 2) )
960 6 : iDataFormat = PACKED10BIT;
961 4 : else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
962 : "16", 2) )
963 2 : iDataFormat = UNPACKED16BIT;
964 2 : else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF,
965 : "08", 2) )
966 2 : 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 10 : 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 10 : L1B_DATASET_NAME_SIZE );
998 10 : szDatasetName[L1B_DATASET_NAME_SIZE] = '\0';
999 :
1000 : // Determine processing center where the dataset was created
1001 10 : if ( EQUALN((const char *)abyRecHeader
1002 : + L1B_NOAA15_HDR_REC_SITE_OFF, "CMS", 3) )
1003 0 : eProcCenter = CMS;
1004 10 : else if ( EQUALN((const char *)abyRecHeader
1005 : + L1B_NOAA15_HDR_REC_SITE_OFF, "DSS", 3) )
1006 4 : eProcCenter = DSS;
1007 6 : else if ( EQUALN((const char *)abyRecHeader
1008 : + L1B_NOAA15_HDR_REC_SITE_OFF, "NSS", 3) )
1009 6 : 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 10 : int iWord = CPL_MSBWORD16( *(GUInt16 *)
1019 : (abyRecHeader + L1B_NOAA15_HDR_REC_ID_OFF) );
1020 10 : switch ( iWord )
1021 : {
1022 : case 2:
1023 2 : eSpacecraftID = NOAA16;
1024 2 : break;
1025 : case 4:
1026 2 : eSpacecraftID = NOAA15;
1027 2 : break;
1028 : case 6:
1029 2 : eSpacecraftID = NOAA17;
1030 2 : break;
1031 : case 7:
1032 2 : eSpacecraftID = NOAA18;
1033 2 : 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 2 : eSpacecraftID = METOP2;
1042 2 : 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 10 : (abyRecHeader + L1B_NOAA15_HDR_REC_PROD_OFF) );
1060 10 : switch ( iWord )
1061 : {
1062 : case 1:
1063 0 : eProductType = LAC;
1064 0 : break;
1065 : case 2:
1066 6 : eProductType = GAC;
1067 6 : break;
1068 : case 3:
1069 2 : eProductType = HRPT;
1070 2 : break;
1071 : case 4: // XXX: documentation specifies the code '4'
1072 : case 13: // for FRAC but real datasets contain '13 here.'
1073 2 : eProductType = FRAC;
1074 2 : 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 10 : (abyRecHeader + L1B_NOAA15_HDR_REC_STAT_OFF) );
1086 :
1087 : // Determine receiving station name
1088 : iWord = CPL_MSBWORD16( *(GUInt16 *)
1089 10 : (abyRecHeader + L1B_NOAA15_HDR_REC_SRC_OFF) );
1090 10 : switch( iWord )
1091 : {
1092 : case 1:
1093 4 : eSource = GC;
1094 4 : break;
1095 : case 2:
1096 2 : eSource = WI;
1097 2 : 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 4 : 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 24 : SetMetadataItem( "DATASET_NAME", szDatasetName );
1121 :
1122 24 : 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 8 : pszText = "NOAA-12(D)";
1150 8 : break;
1151 : case NOAA13:
1152 0 : pszText = "NOAA-13(I)";
1153 0 : break;
1154 : case NOAA14:
1155 6 : pszText = "NOAA-14(J)";
1156 6 : break;
1157 : case NOAA15:
1158 2 : pszText = "NOAA-15(K)";
1159 2 : break;
1160 : case NOAA16:
1161 2 : pszText = "NOAA-16(L)";
1162 2 : break;
1163 : case NOAA17:
1164 2 : pszText = "NOAA-17(M)";
1165 2 : break;
1166 : case NOAA18:
1167 2 : pszText = "NOAA-18(N)";
1168 2 : break;
1169 : case NOAA19:
1170 0 : pszText = "NOAA-19(N')";
1171 0 : break;
1172 : case METOP2:
1173 2 : pszText = "METOP-A(2)";
1174 2 : 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 24 : SetMetadataItem( "SATELLITE", pszText );
1186 :
1187 24 : switch( eProductType )
1188 : {
1189 : case LAC:
1190 2 : pszText = "AVHRR LAC";
1191 2 : break;
1192 : case HRPT:
1193 4 : pszText = "AVHRR HRPT";
1194 4 : break;
1195 : case GAC:
1196 16 : pszText = "AVHRR GAC";
1197 16 : break;
1198 : case FRAC:
1199 2 : pszText = "AVHRR FRAC";
1200 2 : break;
1201 : default:
1202 0 : pszText = "Unknown";
1203 : break;
1204 : }
1205 24 : SetMetadataItem( "DATA_TYPE", pszText );
1206 :
1207 : // Get revolution number as string, we don't need this value for processing
1208 : char szRevolution[6];
1209 24 : memcpy( szRevolution, szDatasetName + 32, 5 );
1210 24 : szRevolution[5] = '\0';
1211 24 : SetMetadataItem( "REVOLUTION", szRevolution );
1212 :
1213 24 : switch( eSource )
1214 : {
1215 : case DU:
1216 0 : pszText = "Dundee, Scotland, UK";
1217 0 : break;
1218 : case GC:
1219 6 : pszText = "Fairbanks, Alaska, USA (formerly Gilmore Creek)";
1220 6 : 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 14 : pszText = "Wallops Island, Virginia, USA";
1235 14 : break;
1236 : default:
1237 4 : pszText = "Unknown receiving station";
1238 : break;
1239 : }
1240 24 : SetMetadataItem( "SOURCE", pszText );
1241 :
1242 24 : switch( eProcCenter )
1243 : {
1244 : case CMS:
1245 0 : pszText = "Centre de Meteorologie Spatiale - Lannion, France";
1246 0 : break;
1247 : case DSS:
1248 4 : pszText = "Dundee Satellite Receiving Station - Dundee, Scotland, UK";
1249 4 : break;
1250 : case NSS:
1251 20 : pszText = "NOAA/NESDIS - Suitland, Maryland, USA";
1252 20 : 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 24 : SetMetadataItem( "PROCESSING_CENTER", pszText );
1261 :
1262 24 : return CE_None;
1263 : }
1264 :
1265 : /************************************************************************/
1266 : /* ComputeFileOffsets() */
1267 : /************************************************************************/
1268 :
1269 32 : int L1BDataset::ComputeFileOffsets()
1270 : {
1271 32 : switch( eProductType )
1272 : {
1273 : case HRPT:
1274 : case LAC:
1275 : case FRAC:
1276 12 : nRasterXSize = 2048;
1277 12 : nBufferSize = 20484;
1278 12 : iGCPStart = 25;
1279 12 : iGCPStep = 40;
1280 12 : nGCPsPerLine = 51;
1281 12 : if ( eL1BFormat == L1B_NOAA9 )
1282 : {
1283 8 : if (iDataFormat == PACKED10BIT)
1284 : {
1285 4 : nRecordSize = 14800;
1286 4 : nRecordDataEnd = 14104;
1287 : }
1288 4 : else if (iDataFormat == UNPACKED16BIT)
1289 : {
1290 2 : switch(nBands)
1291 : {
1292 : case 1:
1293 2 : nRecordSize = 4544;
1294 2 : nRecordDataEnd = 4544;
1295 2 : 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 2 : switch(nBands)
1317 : {
1318 : case 1:
1319 2 : nRecordSize = 2496;
1320 2 : nRecordDataEnd = 2496;
1321 2 : 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 8 : nDataStartOffset = nRecordSize + L1B_NOAA9_HEADER_SIZE;
1341 8 : nRecordDataStart = 448;
1342 8 : iGCPCodeOffset = 52;
1343 8 : iGCPOffset = 104;
1344 : }
1345 :
1346 8 : else if ( eL1BFormat == L1B_NOAA15
1347 : || eL1BFormat == L1B_NOAA15_NOHDR )
1348 : {
1349 4 : if (iDataFormat == PACKED10BIT)
1350 : {
1351 4 : nRecordSize = 15872;
1352 4 : 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 4 : nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
1408 4 : nRecordDataStart = 1264;
1409 4 : iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
1410 4 : iGCPOffset = 640;
1411 : }
1412 : else
1413 0 : return 0;
1414 12 : break;
1415 :
1416 : case GAC:
1417 20 : nRasterXSize = 409;
1418 20 : nBufferSize = 4092;
1419 20 : iGCPStart = 5; // FIXME: depends of scan direction
1420 20 : iGCPStep = 8;
1421 20 : nGCPsPerLine = 51;
1422 20 : if ( eL1BFormat == L1B_NOAA9 )
1423 : {
1424 14 : if (iDataFormat == PACKED10BIT)
1425 : {
1426 6 : nRecordSize = 3220;
1427 6 : nRecordDataEnd = 3176;
1428 : }
1429 8 : else if (iDataFormat == UNPACKED16BIT)
1430 4 : switch(nBands)
1431 : {
1432 : case 1:
1433 0 : nRecordSize = 1268;
1434 0 : nRecordDataEnd = 1266;
1435 0 : break;
1436 : case 2:
1437 2 : nRecordSize = 2084;
1438 2 : nRecordDataEnd = 2084;
1439 2 : 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 2 : nRecordSize = 4540;
1450 2 : nRecordDataEnd = 4538;
1451 : break;
1452 : }
1453 : else // UNPACKED8BIT
1454 : {
1455 4 : switch(nBands)
1456 : {
1457 : case 1:
1458 0 : nRecordSize = 860;
1459 0 : nRecordDataEnd = 858;
1460 0 : break;
1461 : case 2:
1462 2 : nRecordSize = 1268;
1463 2 : nRecordDataEnd = 1266;
1464 2 : 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 2 : nRecordSize = 2496;
1475 2 : nRecordDataEnd = 2494;
1476 : break;
1477 : }
1478 : }
1479 14 : nDataStartOffset = nRecordSize * 2 + L1B_NOAA9_HEADER_SIZE;
1480 14 : nRecordDataStart = 448;
1481 14 : iGCPCodeOffset = 52;
1482 14 : iGCPOffset = 104;
1483 : }
1484 :
1485 12 : else if ( eL1BFormat == L1B_NOAA15
1486 : || eL1BFormat == L1B_NOAA15_NOHDR )
1487 : {
1488 6 : if (iDataFormat == PACKED10BIT)
1489 : {
1490 2 : nRecordSize = 4608;
1491 2 : nRecordDataEnd = 3992;
1492 : }
1493 4 : else if (iDataFormat == UNPACKED16BIT)
1494 : {
1495 2 : 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 2 : nRecordSize = 5632;
1515 2 : nRecordDataEnd = 5354;
1516 : break;
1517 : }
1518 : }
1519 : else // UNPACKED8BIT
1520 : {
1521 2 : 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 2 : nRecordSize = 3584;
1541 2 : nRecordDataEnd = 3309;
1542 : break;
1543 : }
1544 : }
1545 : nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ?
1546 6 : nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE;
1547 6 : nRecordDataStart = 1264;
1548 6 : iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
1549 6 : iGCPOffset = 640;
1550 : }
1551 : else
1552 0 : return 0;
1553 20 : break;
1554 : default:
1555 0 : return 0;
1556 : }
1557 :
1558 32 : return 1;
1559 : }
1560 :
1561 : /************************************************************************/
1562 : /* DetectFormat() */
1563 : /************************************************************************/
1564 :
1565 23852 : int L1BDataset::DetectFormat( GDALOpenInfo *poOpenInfo )
1566 :
1567 : {
1568 23852 : GByte* pabyHeader = poOpenInfo->pabyHeader;
1569 23852 : if (pabyHeader == NULL || poOpenInfo->nHeaderBytes < L1B_NOAA9_HEADER_SIZE)
1570 22004 : return L1B_NONE;
1571 :
1572 : // We will try the NOAA-15 and later formats first
1573 1848 : 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 10 : return L1B_NOAA15;
1582 :
1583 : // Next try the NOAA-9/14 formats
1584 1838 : 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 14 : return L1B_NOAA9;
1592 :
1593 : // Finally try the AAPP formats
1594 1824 : 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 1824 : 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 23852 : GDALDataset *L1BDataset::Open( GDALOpenInfo * poOpenInfo )
1624 :
1625 : {
1626 23852 : int eL1BFormat = DetectFormat( poOpenInfo );
1627 23852 : if ( eL1BFormat == L1B_NONE )
1628 23828 : return NULL;
1629 :
1630 : /* -------------------------------------------------------------------- */
1631 : /* Confirm the requested access is supported. */
1632 : /* -------------------------------------------------------------------- */
1633 24 : 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 24 : const char *pszFilename = poOpenInfo->pszFilename;
1653 :
1654 24 : poDS = new L1BDataset( eL1BFormat );
1655 :
1656 24 : poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
1657 24 : 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 24 : if ( poDS->ProcessDatasetHeader() != CE_None )
1667 : {
1668 0 : CPLDebug( "L1B", "Error reading L1B record header." );
1669 0 : goto bad;
1670 : }
1671 :
1672 24 : VSIStatL(pszFilename, &sStat);
1673 :
1674 24 : 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 16 : for(j=0;j<3;j++)
1685 : {
1686 16 : poDS->iDataFormat = PACKED10BIT + j;
1687 16 : if (!poDS->ComputeFileOffsets())
1688 0 : goto bad;
1689 :
1690 16 : nTempYSize = (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
1691 16 : if (nTempYSize < 5)
1692 2 : continue;
1693 :
1694 14 : int nLastScanlineNumber = 0;
1695 14 : int nDiffLine = 0;
1696 : int i;
1697 66 : for (i=0;i<5;i++)
1698 : {
1699 58 : nScanlineNumber = 0;
1700 :
1701 58 : VSIFSeekL(poDS->fp, poDS->nDataStartOffset + i * poDS->nRecordSize, SEEK_SET);
1702 58 : VSIFReadL(&nScanlineNumber, 1, 2, poDS->fp);
1703 : #ifdef CPL_LSB
1704 58 : CPL_SWAP16PTR( &nScanlineNumber );
1705 : #endif
1706 58 : if (i == 1)
1707 : {
1708 14 : nDiffLine = nScanlineNumber - nLastScanlineNumber;
1709 14 : if (nDiffLine == 0)
1710 0 : break;
1711 : }
1712 44 : else if (i > 1)
1713 : {
1714 30 : if (nDiffLine != nScanlineNumber - nLastScanlineNumber)
1715 6 : break;
1716 : }
1717 :
1718 52 : nLastScanlineNumber = nScanlineNumber;
1719 : }
1720 :
1721 14 : if (i == 5)
1722 : {
1723 : CPLDebug("L1B", "Guessed data format : %s",
1724 : (poDS->iDataFormat == PACKED10BIT) ? "10" :
1725 8 : (poDS->iDataFormat == UNPACKED8BIT) ? "08" : "16");
1726 8 : break;
1727 : }
1728 : }
1729 :
1730 8 : 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 16 : 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 24 : (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
1746 :
1747 : /* -------------------------------------------------------------------- */
1748 : /* Create band information objects. */
1749 : /* -------------------------------------------------------------------- */
1750 : int iBand, i;
1751 :
1752 130 : for( iBand = 1, i = 0; iBand <= poDS->nBands; iBand++ )
1753 : {
1754 106 : poDS->SetBand( iBand, new L1BRasterBand( poDS, iBand ));
1755 :
1756 : // Channels descriptions
1757 212 : if ( poDS->eSpacecraftID >= NOAA6 && poDS->eSpacecraftID <= METOP3 )
1758 : {
1759 106 : if ( !(i & 0x01) && poDS->iChannelsMask & 0x01 )
1760 : {
1761 20 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[0] );
1762 20 : i |= 0x01;
1763 20 : continue;
1764 : }
1765 86 : if ( !(i & 0x02) && poDS->iChannelsMask & 0x02 )
1766 : {
1767 24 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[1] );
1768 24 : i |= 0x02;
1769 24 : continue;
1770 : }
1771 62 : if ( !(i & 0x04) && poDS->iChannelsMask & 0x04 )
1772 : {
1773 30 : if ( poDS->eSpacecraftID >= NOAA15
1774 : && poDS->eSpacecraftID <= METOP3 )
1775 10 : if ( poDS->iInstrumentStatus & 0x0400 )
1776 10 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[7] );
1777 : else
1778 0 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[6] );
1779 : else
1780 10 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[2] );
1781 20 : i |= 0x04;
1782 20 : continue;
1783 : }
1784 42 : if ( !(i & 0x08) && poDS->iChannelsMask & 0x08 )
1785 : {
1786 22 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] );
1787 22 : i |= 0x08;
1788 22 : continue;
1789 : }
1790 20 : if ( !(i & 0x10) && poDS->iChannelsMask & 0x10 )
1791 : {
1792 20 : if (poDS->eSpacecraftID == NOAA13) // 5 NOAA-13
1793 0 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[5] );
1794 20 : 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 20 : poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[4] );
1800 20 : i |= 0x10;
1801 20 : continue;
1802 : }
1803 : }
1804 : }
1805 :
1806 : /* -------------------------------------------------------------------- */
1807 : /* Do we have GCPs? */
1808 : /* -------------------------------------------------------------------- */
1809 : if ( 1/*EQUALN((const char *)pabyTBMHeader + 96, "Y", 1)*/ )
1810 : {
1811 24 : 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 24 : poDS->SetDescription( poOpenInfo->pszFilename );
1841 24 : poDS->TryLoadXML();
1842 :
1843 : /* -------------------------------------------------------------------- */
1844 : /* Check for external overviews. */
1845 : /* -------------------------------------------------------------------- */
1846 24 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
1847 :
1848 24 : return( poDS );
1849 :
1850 : bad:
1851 0 : delete poDS;
1852 0 : return NULL;
1853 : }
1854 :
1855 : /************************************************************************/
1856 : /* GDALRegister_L1B() */
1857 : /************************************************************************/
1858 :
1859 1135 : void GDALRegister_L1B()
1860 :
1861 : {
1862 : GDALDriver *poDriver;
1863 :
1864 1135 : if( GDALGetDriverByName( "L1B" ) == NULL )
1865 : {
1866 1093 : poDriver = new GDALDriver();
1867 :
1868 1093 : poDriver->SetDescription( "L1B" );
1869 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1870 1093 : "NOAA Polar Orbiter Level 1b Data Set" );
1871 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1872 1093 : "frmt_l1b.html" );
1873 :
1874 1093 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1875 :
1876 1093 : poDriver->pfnOpen = L1BDataset::Open;
1877 :
1878 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
1879 : }
1880 1135 : }
1881 :
|