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