OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
HawkeyeDecode.c
Go to the documentation of this file.
1 /*
2  HawkeyeDecode.c
3 
4  Matt Longmire
5  Cloudland Instruments
6 
7  Contains the utility functions for scanning and
8  decoding a Hawkeye Image Format file (stream)
9 
10 */
11 #include "HawkeyeDecode.h"
12 #include <string.h>
13 #include <stdio.h>
14 
15 #ifdef _DEBUG
16  #define DEBUG_LOG 1
17 #else
18  #define DEBUG_LOG 0
19 #endif
20 
21 #define HEADER_DATA_LEN 7
22 
23 
24 /*
25  RowInfo:
26 
27  Struct for holding the decoded parameters associated with
28  a row of pixel data.
29 
30 */
31 typedef struct {
32  uint16_t ls5Bits;
33  uint16_t width;
34  uint16_t rowNumber;
35  uint32_t timeStamp;
36  uint16_t noDarksAtStart;
37  uint16_t isSpectral;
38  uint16_t isDark;
39  uint16_t dataLen;
40  uint16_t electronicGain;
41  uint16_t slope1, slope2;
42  uint16_t knee;
43  uint8_t *pPixels;
44 } RowInfo;
45 
46 /*
47  HeaderInfo:
48 
49  Struct for holding the parameters found in
50  the header to each record.
51 
52 */
53 
54 static HawkeyeStreamInfo scannedStreamInfo;
55 static uint32_t finderscopeOffsets[MAX_FINDERSCOPE_IMAGES];
56 static uint8_t *scannedStream;
57 static uint8_t missionLog[MISSION_LOG_LENGTH];
58 
59 /*
60  ChecksumRecord:
61 
62  Calculate and retyurn the 16-bit uint16_t
63  checksum of a block.
64 
65 */
66 static uint16_t ChecksumRecord(uint8_t *stream, uint16_t len)
67 {
68  uint16_t sum = 0, recSum;
69  int i;
70 
71  for (i = 0; i < len; i++)
72  sum += stream[i];
73  recSum = stream[i] << 8;
74  recSum += stream[i + 1];
75  return sum == recSum;
76 }
77 
78 /*
79  FindHeader:
80 
81  Scan the stream and return the starting position of the next
82  found header. Verifies the checksum and fills in the
83  HeaderInfo struct.
84 
85 */
86 static uint8_t HAWKEYE_HEADER_START[] = { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
87 uint32_t FindHeader(uint8_t *stream, uint32_t streamLength, uint32_t startPos, HeaderInfo *header)
88 {
89  uint32_t i, j;
90  uint8_t uc;
91  uint32_t ul;
92  uint16_t us;
93 
94  memset(header, 0, sizeof(HeaderInfo));
95  for (i = startPos; i <= streamLength - sizeof(HAWKEYE_HEADER_START); i++)
96  {
97  uc = stream[i]; //get the next byte
98  if (uc == HAWKEYE_HEADER_START[0]) { //does it match the fisrt byte of the header start sequence?
99  for (j = 1; j < sizeof(HAWKEYE_HEADER_START); j++) { //check the full header start sequence length
100  if ((i+j) >= scannedStreamInfo.hebBufSize) {
101  // Can't find header
102  return(0);
103  }
104  if (stream[i + j] != HAWKEYE_HEADER_START[j]) //does it match?
105  break; //if not start over at the next byte in the stream
106  }
107  if (j == sizeof(HAWKEYE_HEADER_START)) { //have we checked all the header start bytes?
108  j += i; //move past the start sequence
109  if (j + HEADER_DATA_LEN > streamLength) //we need 7 bytes from the header
110  return streamLength; //too short so bail
111  header->blockType = stream[j++]; //decode the block type
112  ul = ((uint32_t)stream[j++]) << 24; //decode the record number
113  ul += ((uint32_t)stream[j++]) << 16;
114  ul += ((uint32_t)stream[j++]) << 8;
115  ul += stream[j++];
116  ul = (ul & 0x0FFFFFF0) >> 4;//24-bits in 32-bit field
117  header->recordNo = ul;
118  us = ((uint16_t)stream[j++]) << 8; //decode the block len
119  us += stream[j++]; //this includes the start, header, data and checksum
120  header->blockLen = us;
121  if (i + us > streamLength) //is there enough data left for the payload and checksum?
122  return streamLength; //too short, bail
123  if (!ChecksumRecord(stream + i, us - 2)) //does the checksum match?
124  continue; //if not start at next byte in stream
125  return i; //otherwise return the count to the 1st byte in the header
126  }
127  }
128  }
129  return streamLength;
130 }
131 
132 /*
133  DecodeImageParams:
134 
135  Decode an Image Parameters record, filling in the
136  passed HawkeyeImageInfo struct.
137 
138 */
139 static int DecodeImageParams(uint8_t *stream, uint32_t blockLen, uint32_t startPos, HawkeyeImageInfo *pImgInfo)
140 {
141  uint32_t ul;
142  uint16_t us;
143  uint8_t uc;
144  uint64_t ll;
145 #if INCLUDE_GPS
146  uint64_t inBits, ull;
147  int i, noInBits, gpsCount;
148 #endif
149  memset(pImgInfo, 0, sizeof(HawkeyeImageInfo));
150 #if INCLUDE_GPS
151  if (blockLen != 44 + 34 * 4)
152  return -1;
153 #else
154  if (blockLen != 44)
155  return -1;
156 #endif
157  ul = (uint32_t)stream[startPos++] << 24; //Decode Exposure ID
158  ul += (uint32_t)stream[startPos++] << 16;
159  ul += (uint32_t)stream[startPos++] << 8;
160  ul += stream[startPos++];
161  pImgInfo->exposureID = ul;
162  uc = stream[startPos++]; //Decode Binnings
163  pImgInfo->spectralBinning = uc & 0x0F;
164  pImgInfo->finderscopeBinning = (uc >> 4);
165  ul = (uint32_t)stream[startPos++] << 24; //Decode Image ID
166  ul += (uint32_t)stream[startPos++] << 16;
167  ul += (uint32_t)stream[startPos++] << 8;
168  ul += stream[startPos++];
169  pImgInfo->imageID = ul;
170  us = (uint16_t)stream[startPos++] << 8; //Decode Channels
171  us += stream[startPos++];
172  pImgInfo->channelBitfield = us;
173  ll = (uint64_t)stream[startPos++] << 40; //Decode Epoch Time
174  ll += (uint64_t)stream[startPos++] << 32;
175  ll += (uint64_t)stream[startPos++] << 24;
176  ll += (uint64_t)stream[startPos++] << 16;
177  ll += (uint64_t)stream[startPos++] << 8;
178  ll += stream[startPos++];
179  ll = (ll & 0x0FFFFFFFFFF0) >> 4;
180  pImgInfo->epochT0 = ll;
181  ul = (uint32_t)stream[startPos++] << 24; //Decode Delta-Host time
182  ul += (uint32_t)stream[startPos++] << 16;
183  ul += (uint32_t)stream[startPos++] << 8;
184  ul += stream[startPos++];
185  pImgInfo->hostDeltaEpoch = (ul >> 4) & 0xFFFFFF;
186  ul = (uint32_t)stream[startPos++] << 24; //Decode Delta-Hawkeye time
187  ul += (uint32_t)stream[startPos++] << 16;
188  ul += (uint32_t)stream[startPos++] << 8;
189  ul += stream[startPos++];
190  pImgInfo->hawkeyeDeltaEpoch = (ul >> 4) & 0xFFFFFF;
191  us = (uint16_t)stream[startPos++] << 8; //Decode CCD 1 Exposure
192  us += stream[startPos++];
193  pImgInfo->ccd1Exposure = us;
194  us = (uint16_t)stream[startPos++] << 8; //Decode CCD 2 Exposure
195  us += stream[startPos++];
196  pImgInfo->ccd2Exposure = us;
197  us = (uint16_t)stream[startPos++] << 8; //Decode CCD 3 Exposure
198  us += stream[startPos++];
199  pImgInfo->ccd3Exposure = us;
200  us = (uint16_t)stream[startPos++] << 8; //Decode CCD 4 Exposure
201  us += stream[startPos++];
202  pImgInfo->ccd4Exposure = us;
203  us = (uint16_t)stream[startPos++] << 8; //Decode Spectral Height
204  us += stream[startPos++];
205  pImgInfo->height = us;
206  us = (uint16_t)stream[startPos++] << 8; //Decode Spectral Interval
207  us += stream[startPos++];
208  pImgInfo->interval = us;
209  uc = stream[startPos++]; //Decode Dark Height / Oversampling
210  pImgInfo->oversampling = (uc & 0x03) + 1; // Oversampling (0-3 => 1-4)
211  pImgInfo->darkHeight = (uc >> 2); // Dark Height
212  if (pImgInfo->darkHeight == 63) // only room for 6 bits so 64
213  pImgInfo->darkHeight = 64; // was encoded as 63
214  us = (uint16_t)stream[startPos++] << 8; //Decode Finderscope Exposure
215  us += stream[startPos++];
216  pImgInfo->finderscopeExposure = us;
217  uc = stream[startPos++]; //Decode No Finderscope Images
218  pImgInfo->noFinderscopeImages = uc;
219  uc = stream[startPos++]; //Decode bitfield
220  if ((uc & 03) == 1)
221  pImgInfo->compression = DC_PACKED;
222  else if ((uc & 03) == 2)
223  pImgInfo->compression = DC_DELTA;
224  else
225  pImgInfo->compression = DC_UNCOMPRESSED;
226  if (uc & 0x04) pImgInfo->darkSubtracted = 1;
227  pImgInfo->readoutOrder = (uc & 0x08) ? RO_BLUE_FIRST : RO_GREEN_FIRST;
228  pImgInfo->shutterSolenoid = (uc >> 4) & 0x03; //Shutter solenoid used
229  us = (uint16_t)stream[startPos++] << 8; //Decode Error Code
230  us += stream[startPos++];
231  pImgInfo->errorCode = us;
232 #if INCLUDE_GPS
233  inBits = 0;
234  gpsCount = noInBits = 0;
235  for (i = 0; i < 34; i++) //Decode 34 32-bit GPS Binary fields
236  {
237  ul = (uint32_t)stream[startPos++] << 24; // Get the next 32-bit field
238  ul += (uint32_t)stream[startPos++] << 16;
239  ul += (uint32_t)stream[startPos++] << 8;
240  ul += stream[startPos++];
241  ull = (ul & 0x3FFFFFFC) >> 2; //extract 28 bits, throwing away leading 0 and trailing 10
242  inBits = inBits | (ull << (12 - noInBits)); //add 28-bits to 40-bit bit field
243  noInBits += 28; //just added 28 bits
244  while (noInBits >= 8) {
245  pImgInfo->gpsBinary[gpsCount++] = (uint8_t)(inBits >> 32); //peel off ms 8 bits
246  inBits = (inBits & 0x00FFFFFFFF) << 8; //keep ls 32 bits and move to ms bits
247  noInBits -= 8; //just ate 8 bits
248  if (gpsCount == 116) //have we decoded 116 bytes?
249  goto gps_done; // if so we're done
250  }
251  }
252 gps_done:
253 #endif
254  return 0;
255 }
256 
257 /*
258  DecodeTelemetry:
259 
260  Decode a Telemetry Record, filling in the
261  HawkeyeTelemetryInfo struct.
262 
263 */
264 static int DecodeTelemetry(uint8_t *stream, uint32_t blockLen, uint32_t startPos, HawkeyeTelemetryInfo *pTelem)
265 {
266  int i;
267  uint32_t ul;
268  uint16_t us;
269  int noChannels;
270 
271  memset(pTelem, 0, sizeof(HawkeyeTelemetryInfo)); //zero the struct
272  noChannels = (blockLen - 4) / 3; //calculate no channels reported
273  if (noChannels > TC_NO_CHANNELS) //is it more than we know about?
274  noChannels = TC_NO_CHANNELS; // if so limit it
275  ul = (uint32_t)stream[startPos++] << 24; //decode the time stamp
276  ul += (uint32_t)stream[startPos++] << 16;
277  ul += (uint32_t)stream[startPos++] << 8;
278  ul += (uint32_t)stream[startPos++];
279  ul = (ul & 0x0FFFFFF0) >> 4; //24-bit in 32-bit field
280  pTelem->timeStamp = ul;
281  for (i = 0; i < noChannels; i++) { //for each channel
282  us = (uint32_t)stream[startPos++] << 8; //decode the value
283  us += (uint32_t)stream[startPos++];
284  pTelem->telemetry.channel[i].channelValue = us;
285  us = (uint32_t)stream[startPos++]; //and decode the interpretation
286  pTelem->telemetry.channel[i].channelInterp = us;
287  }
288  pTelem->noChannels = noChannels; //record the no channels decoded
289  return 0;
290 }
291 
292 
293 /*
294  DecodeMissionLog:
295 
296  Decode a Mission Log Record
297 
298 */
299 static int DecodeMissionLog(uint8_t *stream, uint32_t blockLen, uint32_t startPos, uint8_t *dest)
300 {
301  memset(dest, 0, MISSION_LOG_LENGTH); //zero the results
302  if ( blockLen > MISSION_LOG_LENGTH - 1) //leave at least 1 byte for NULL terminator
303  blockLen = MISSION_LOG_LENGTH - 1;
304  memcpy(dest, stream + startPos, blockLen); //extract text string
305  return 0;
306 }
307 
308 /*
309  ScanRow:
310 
311  Scan the Umcompressed or Compressed Pixels record,
312  filling in the passed RowInfo struct. No pixel
313  decoding is done here.
314 
315 */
316 static int ScanRow(uint8_t *stream, uint32_t blockLen, uint32_t startPos, RowInfo *pRow)
317 {
318  uint8_t uc, ls5Bits, isSpectral, isDark;
319  uint16_t us;
320  uint32_t ul;
321  int pos = startPos;
322 
323  memset(pRow, 0, sizeof(RowInfo));
324  if (blockLen < 10)
325  return -1;
326  uc = stream[pos++]; //get the flags
327  isDark = (uc & 0x80) ? 1 : 0;//decode dark row flag
328  isSpectral = (uc & 0x40) ? 1 : 0;//decode spectral flag
329  ls5Bits = (uc & 0x01F); //decode ls 5 bits
330  if (isSpectral && (ls5Bits < 1 || ls5Bits > 8))
331  return -1;
332  if (!isSpectral && (ls5Bits < 1 || ls5Bits > FINDERSCOPE_MAX_IMAGES))
333  return -1;
334  pRow->ls5Bits = ls5Bits;
335  pRow->isDark = isDark;
336  pRow->isSpectral = isSpectral;
337  ul = (uint32_t)stream[pos++] << 24; //decode timestamp
338  ul += (uint32_t)stream[pos++] << 16;
339  ul += (uint32_t)stream[pos++] << 8;
340  ul += stream[pos++];
341  ul = (ul & 0x0FFFFFF0) >> 4;
342  pRow->timeStamp = ul;
343  us = (uint16_t)stream[pos++] << 8; //decode Row Number
344  us += stream[pos++];
345  pRow->rowNumber = us;
346  us = (uint16_t)stream[pos++] << 8; //decode Row Width
347  us += stream[pos++];
348  pRow->width = us;
349  uc = stream[pos++]; //decode no darks at start
350  pRow->noDarksAtStart = uc;
351  uc = stream[pos++]; //decode electronic gain
352  pRow->electronicGain = uc;
353  if (isSpectral) {
354  us = (uint16_t)stream[pos++] << 8; //decode slope 1
355  us += stream[pos++];
356  pRow->slope1 = us;
357  us = (uint16_t)stream[pos++] << 8; //decode slope 2
358  us += stream[pos++];
359  pRow->slope2 = us;
360  us = (uint16_t)stream[pos++] << 8; //decode knee
361  us += stream[pos++];
362  pRow->knee = us;
363  } else {
364  pRow->slope1 = us;
365  pRow->slope2 = us;
366  pRow->knee = 65535;
367  }
368  pRow->pPixels = stream + pos;
369  pRow->dataLen = (uint16_t)(blockLen - (pos - startPos));
370  return 0;
371 }
372 
373 /*
374  DecodeEOF:
375 
376  Decode the End of File record, returning
377  the expected file length.
378 
379 */
380 static int DecodeEOF(uint8_t *stream, uint32_t blockLen, uint32_t startPos, int32_t *pExpLen)
381 {
382  uint32_t ul;
383  *pExpLen = 0;
384  if (blockLen != 4)
385  return -1;
386  ul = (uint32_t)stream[startPos++] << 24;
387  ul += (uint32_t)stream[startPos++] << 16;
388  ul += (uint32_t)stream[startPos++] << 8;
389  ul += (uint32_t)stream[startPos++];
390  *pExpLen = ul;
391  return 0;
392 }
393 
394 /*
395  HawkeyeScanStream:
396 
397  Scan the Hawkeye Image File Stream (data bytes) and fill in
398  the HawkeyeStreamInfo struct based on what we find. No images
399  are stored here, just scanned and detected.
400 */
401 HAWKEYE_STREAM_ERROR HawkeyeScanStream(uint8_t *stream, uint32_t streamLength, HawkeyeStreamInfo *streamInfo)
402 {
404  uint32_t streamPos;
405  uint32_t headerPos, nextPos;
406  uint32_t blockLen;
407  ENCODED_BLOCK_TYPE blockType;
408  HawkeyeTelemetryInfo telem;
409  HawkeyeImageInfo imgInfo;
410  RowInfo rowInfo;
411  int blockRes;
412  int lastFSI = -1, missingRows;
413  int noFSI = 0, band;
414  int32_t expectedLen;
415  int32_t missing;
416  int imageParamsFound = 0;
417  uint32_t recNo, nextRecNo = 0;
418  HeaderInfo header;
419  int firstLight[8];
420  int darkHeight[8];
421  uint32_t maxBlockLen = 0;
422  //ENCODED_BLOCK_TYPE maxBlockType = EBT_NULL;
423 
424 #if DEBUG_LOG
425  FILE *fp;
426  fp = fopen("HawkeyeDecodeLog.txt", "wt");
427 #endif
428 
429  memset(streamInfo, 0, sizeof(HawkeyeStreamInfo)); //zero the results
430  memset(finderscopeOffsets, 0, sizeof(finderscopeOffsets)); //and where we found each finderscope image
431  memset(firstLight, 0, sizeof(firstLight)); //init the dark counters
432  memset(darkHeight, 0, sizeof(darkHeight));
433  scannedStream = stream; //remember the stream for when we extract components later
434  streamInfo->streamLength = streamLength;
435  streamInfo->hebBufSize = streamLength;
436  scannedStreamInfo.hebBufSize = streamLength;
437  streamInfo->noMissingBytes = -1;
438 
439  // Find the first valid Image Parameters
440  streamPos = 0;
441  headerPos = FindHeader(stream, streamLength, streamPos, &header);//find the first valid header
442  while (streamPos < streamLength) { //process until we are done
443  blockType = header.blockType; //remember what record type we found
444  blockLen = header.blockLen; //remember the length of the records data block
445  streamPos = headerPos + sizeof(HAWKEYE_HEADER_START)+HEADER_DATA_LEN; //skip passed this recordheader
446  nextPos = FindHeader(stream, streamLength, streamPos, &header); //and find the next
447  if (blockType != EBT_EOF && nextPos - headerPos != blockLen) { //is the next headr at the right position or is this an EOF
448  streamPos = headerPos = nextPos; // and try the next record
449  continue;
450  }
451  blockLen -= 9 + sizeof(HAWKEYE_HEADER_START); //skip the record headr bytes
452  if (blockType == EBT_IMAGE_PARAMS) {
453  if ((blockRes = DecodeImageParams(stream, blockLen, streamPos, &imgInfo)) == 0) { //if so decode it
454  streamInfo->imageInfo = imgInfo; //and record it
455  imageParamsFound = 1;
456  break;
457  }
458  }
459  streamPos = headerPos = nextPos; //skip to next header
460  }
461  if ( !imageParamsFound )
463  else {
464  stream = scannedStream; //start over at the beginning
465  streamPos = 0;
466  headerPos = FindHeader(stream, streamLength, streamPos, &header); //find the first valid header
467  while (streamPos < streamLength) { //process until we are done
468  blockType = header.blockType; //remember what record type we found
469  recNo = header.recordNo; //and the record number
470  if (recNo > nextRecNo) //have we missed any records?
471  streamInfo->noMissingRecords += recNo - nextRecNo; // if so keep track of that
472  nextRecNo = recNo + 1; //the next record number we expect to find
473  blockLen = header.blockLen; //remember the length of the records data block
474  streamPos = headerPos + sizeof(HAWKEYE_HEADER_START)+HEADER_DATA_LEN; // sko passed this recordheader
475  nextPos = FindHeader(stream, streamLength, streamPos, &header); //and find the next
476  if (blockType != EBT_EOF && nextPos - headerPos != blockLen) { //is the next headr at the right position or is this an EOF
477  streamInfo->noBadRecords++; // if no recod this a a bad record
478  streamPos = headerPos = nextPos; // and try the next record
479  continue;
480  }
481  streamInfo->noRecords = recNo + 1; //we've found a good record
482  if (blockLen > maxBlockLen) { //keep track of the longest block length
483  maxBlockLen = blockLen;
484  //maxBlockType = blockType;
485  }
486  blockLen -= 9 + sizeof(HAWKEYE_HEADER_START); //skip the record headr bytes
487  blockRes = 0; //assume it's a good block
488  switch (blockType) { //what type of block did we find?
489  case EBT_UNCOMPRESSED: //Uncompressed or
490  case EBT_COMPRESSED: //Compressed Pixel Data?
491  if ((blockRes = ScanRow(stream, blockLen, streamPos, &rowInfo)) == 0) { //if so scan the record
492  if (!rowInfo.isSpectral) { //is this Finderscope data?
493  noFSI = rowInfo.ls5Bits - 1; // if so get the finderscope image no
494  if (noFSI < MAX_FINDERSCOPE_IMAGES) { //do we have room for it?
495  if (noFSI != lastFSI) { //have we jumped to the next image?
496 #if DEBUG_LOG
497  fprintf(fp, "Finderscope Image %d\n", noFSI + 1);
498 #endif
499  finderscopeOffsets[noFSI] = headerPos;
500  // printf("finderscopeOffsets: %d\n", headerPos);
501 
502  //remember it's position so it's faster to retrieve
503  streamInfo->noFinderscopeImages = noFSI + 1; //count it
504  // printf("# FS2: %d\n", streamInfo->noFinderscopeImages);
505  streamInfo->finderscopeInfo[noFSI].timeStamp = rowInfo.timeStamp; //and record the timestamp
506  }
507  if (streamInfo->finderscopeInfo[noFSI].width == 0) { //have we detected the width yet?
508  streamInfo->finderscopeInfo[noFSI].width = rowInfo.width; //if not record it and the electronic gain
509  streamInfo->finderscopeInfo[noFSI].electronicGain = rowInfo.electronicGain / 10.0;
510  }
511  missingRows = rowInfo.rowNumber - streamInfo->finderscopeInfo[noFSI].height;
512  if (missingRows > 0) //any missing rows?
513  streamInfo->finderscopeInfo[noFSI].noMissingRows += missingRows; //if so count them
514  if (missingRows >= 0) //is this a new row
515  streamInfo->finderscopeInfo[noFSI].height = rowInfo.rowNumber + 1; // if so add it to the height
516  }
517  lastFSI = noFSI; //remember this image number
518  }
519  else { //otherwise this is Spectral Band pixel data
520 #if DEBUG_LOG
521  fprintf(fp, "Spectral Band %d %s Row %d\n", rowInfo.ls5Bits,
522  rowInfo.isDark ? "Dark " : "Light",
523  rowInfo.rowNumber);
524 #endif
525  band = rowInfo.ls5Bits - 1; //remember which band it came from
526  if (streamInfo->spectralInfo[band].width == 0) { //have we not seen this band before
527  streamInfo->spectralInfo[band].width = rowInfo.width; //if so record the width
528  streamInfo->spectralInfo[band].timeStamp = rowInfo.timeStamp; //and the timestamp and electronic gain
529  streamInfo->spectralInfo[band].electronicGain = rowInfo.electronicGain / 100.0;
530  }
531  if (rowInfo.isDark) //is it a dark row?
532  streamInfo->spectralInfo[band].averageDarkRowPresent = 1; //let them know we found it
533  else {
534  if (streamInfo->spectralInfo[band].height == 0) //is this the first light we've seen
535  firstLight[band] = rowInfo.rowNumber; // if so remember it
536  missingRows = rowInfo.rowNumber - streamInfo->spectralInfo[band].height;
537  if (missingRows > 0) //did we miss any rows?
538  streamInfo->spectralInfo[band].noMissingRows += missingRows; //if so keep track
539  if (missingRows >= 0) //is this a new row?
540  streamInfo->spectralInfo[band].height = rowInfo.rowNumber + 1; //if so it adds to the height
541  }
542  }
543  };
544  break;
545  case EBT_IMAGE_PARAMS: //is the record Image Parameters?
546  if ((blockRes = DecodeImageParams(stream, blockLen, streamPos, &imgInfo)) == 0) { //if so decode it
547 #if DEBUG_LOG
548  fprintf(fp, "Image Parameters\n");
549 #endif
550  }
551  break;
552  case EBT_TELEMETRY: //is the record Telemetry Data
553  if ((blockRes = DecodeTelemetry(stream, blockLen, streamPos, &telem)) == 0) { //if so Decode it
554 #if DEBUG_LOG
555  fprintf(fp, "Telemetry Record\n");
556 #endif
557  if (streamInfo->noTelemetryRecords < MAX_TELEMETRY_RECORDS) { //do we have room for it?
558  streamInfo->telemetryInfo[streamInfo->noTelemetryRecords] = telem; //if so record it
559  streamInfo->noTelemetryRecords++; //and count it
560  }
561  }
562  break;
563  case EBT_MISSION_LOG: //is the record the Mission Log?
564  if ((blockRes = DecodeMissionLog(stream, blockLen, streamPos, missionLog)) == 0) { //if so Decode it
565 #if DEBUG_LOG
566  fprintf(fp, "Mission Log Record\n");
567 #endif
568  if ( strlen(streamInfo->missionLog) == 0) //do we have it already
569  memcpy(streamInfo->missionLog, missionLog, MISSION_LOG_LENGTH); //if not record it
570  }
571  break;
572  case EBT_EOF: //is the record and EOF?
573  if ((blockRes = DecodeEOF(stream, blockLen, streamPos, &expectedLen)) == 0) { //if so decode it
574 #if DEBUG_LOG
575  fprintf(fp, "EOF Record\n");
576 #endif
577  missing = expectedLen - (streamPos + 6);
578  if (missing >= 0) //any bytes missing
579  streamInfo->noMissingBytes = missing; // if so record them
580  streamInfo->streamLength = expectedLen; //and record the expected length
581  }
582  break;
583  default: //it was an unknown record
584 #if DEBUG_LOG
585  fprintf(fp, "Unknown Record\n");
586 #endif
587  streamInfo->noUnknownRecords++; //count it as such
588  break;
589  }
590  if (blockRes != 0) //was there an error in decoding the block
591  streamInfo->noBadRecords++; // if so count it as such
592  streamPos = headerPos = nextPos;
593  if (blockType == EBT_EOF)
594  break;
595  } // while not EOF or more bytes to process
596  for (band = 0; band < 8; band++) { //for each of the spectral bands
597  if (streamInfo->spectralInfo[band].height != 0) { //did we see any row data?
598  streamInfo->noSpectralImages++; //if so then count it
599  }
600  }
601  scannedStreamInfo = *streamInfo; //remember the stream info so we return so we can reuse it
602  }
603 #if DEBUG_LOG
604  fclose(fp);
605 #endif
606  return res;
607 }
608 
609 /*
610  UncompressKLIPixels:
611 
612  Uncompress a row of Spectral Pixels that were compressed
613  using the dual-slope + knee method, then otionally
614  delta encoded and then finally packed.
615 */
616 static void UncompressKLIPixels(uint16_t *pixels, uint8_t *stream, RowInfo rowInfo)
617 {
618  uint16_t i, p0=0, p1, p2, vid;
619  int d1, d2;
620  uint32_t bits=0;
621  int noBits;
622  uint32_t lvid, s1, s2, v1;
623  int width = rowInfo.width;
624  uint32_t eGain;
625 
626  // Init compression params
627  s1 = rowInfo.slope1 * 10; //1st slope, e-/ADU x 100
628  s2 = rowInfo.slope2 * 10; //2nd slope, e-/ADU x 100
629  eGain = rowInfo.electronicGain; // KLI EGain, e-/ADU x 100
630  v1 = rowInfo.knee; //Knee ADU
631 
632  if (scannedStreamInfo.imageInfo.compression == DC_PACKED) { //is the data jsut packed (no delta compression)
633  // Unpack the data 12-bit fields in the 8-bit byte stream
634  noBits = 0;
635  for (i = 0; i < width;) { //for each pixel in the row
636  while (noBits < 12) { //make sure we have at least 12 bits
637  bits = bits | (*stream++ << (24 - noBits)); //if not get 8 more bits
638  noBits += 8; //and keep count
639  }
640  vid = bits >> 20; //peel off the 12-bit pixle value
641  bits = bits << 12; //keep the others
642  noBits -= 12; //keep count
643  pixels[i++] = vid & 0xFFF; //and store the pixel
644  }
645  } else { //other wise the data is packed and delat encoded
646  // Unpack the data 13-bit fields in the 8-bit byte stream
647  noBits = 0;
648  for (i = 0; i < width;) { //for each pixel in the row
649  while (noBits < 13) { //make sure we have at least 13 bits
650  bits = bits | (*stream++ << (24 - noBits)); //if not get another 8 bits
651  noBits += 8; //and keep count
652  }
653  vid = bits >> 19; //peel off the 13-bit encoded value
654  bits = bits << 13; //keep the others
655  noBits -= 13; //and keep count
656  if (vid & 0x1000) { //is the flag bit (msb) set?
657  p0 = vid & 0xFFF; // if so it's just 1 12-bit pixle value
658  pixels[i++] = p0; // so save it
659  } else { //otherwise the flag bit (msb) is clear
660  d1 = (vid >> 6) & 0x3F; //so we have two 6-bit delats in the lower 12-bits
661  if (d1 & 0x20) d1 = -64 + d1; //sign exten the first delat
662  p1 = p0 + d1; //calculate the resulting pixel value
663  d2 = (vid & 0x3F); //extract the 2nd 6-bit delta
664  if (d2 & 0x20) d2 = -64 + d2; //sign extend it
665  p0 = p2 = p1 + d2; //calculate the 2nd pixel value
666  pixels[i++] = p1; //store the first
667  pixels[i++] = p2; //and the 2nd
668  }
669  }
670  }
671 
672  // decompress the data
673  for (i = 0; i < width; i++) { //for each pixel in the row
674  lvid = pixels[i]; //get the dual-slope encoded value
675  lvid = (lvid * s1) / eGain; //decode the 1st slope encoding
676  if (lvid > v1) { //was it above the knee
677  lvid -= v1; // if so subtract the knee
678  lvid = lvid * s2 / s1; // decode the 2nd slope encoding
679  lvid += v1; // and add the 2nd slope offset
680  }
681  pixels[i] = (uint16_t)(lvid > 65535 ? 65535 : lvid); //finally clip and save the decoded pixel value
682  }
683 }
684 
685 /*
686  HawkeyeDecodeSpectralImage:
687 
688  Decode a Spectral Band image, saving the data as an array of
689  uint16_ts in to the passed buffer.
690 
691  For each row, the first two unsigend shorts represent the time
692  code offset from start of image with the ms 16-bits first
693  followed by the ls 16-bits.
694 
695  After the time code offset the row pixel data is stored.
696 
697 */
698 HAWKEYE_STREAM_ERROR HawkeyeDecodeSpectralImage(uint16_t bandNo, uint16_t *pixels, uint32_t pixelLength,
699  uint16_t *averageDarkPixels, uint16_t averageDarkPixelLength)
700 {
702  uint32_t streamPos;
703  uint32_t headerPos, nextPos;
704  uint32_t blockLen;
705  ENCODED_BLOCK_TYPE blockType;
706  RowInfo rowInfo;
707  uint32_t pixelPos = 0;
708  int i;
709  uint16_t pix;
710  uint8_t *pPix;
711  HeaderInfo header;
712  uint16_t darkStored = 0;
713 
714  memset(pixels, 0, 2 * pixelLength); //zero the results
715  streamPos = 0;
716  headerPos = FindHeader(scannedStream, scannedStreamInfo.streamLength, streamPos, &header); //find the first record header
717  while (streamPos < scannedStreamInfo.streamLength) { //while there's bytes left
718  blockType = header.blockType; //remember the block type
719  blockLen = header.blockLen; //and length
720  streamPos = headerPos + sizeof(HAWKEYE_HEADER_START)+HEADER_DATA_LEN; //move passed this record header
721  nextPos = FindHeader(scannedStream, scannedStreamInfo.streamLength, streamPos, &header); //and find the next
722  if (nextPos<=streamPos) { // avoid infinite loop, LH, Jan. 19, 2021
723  printf("HawkeyeDecodeSpectralImage error.\n");
724  break;
725  }
726  if (nextPos - headerPos != blockLen) { //is the next header at the right postiton?
727  streamPos = headerPos = nextPos; // if not then skip this one
728  continue;
729  }
730  blockLen -= 9 + sizeof(HAWKEYE_HEADER_START); //account fo the header bytes
731  if ( blockType== EBT_UNCOMPRESSED || blockType == EBT_COMPRESSED ) { //did we find a Pixel Data record?
732  if (ScanRow(scannedStream, blockLen, streamPos, &rowInfo) == 0) { //if so then scan it
733  if (rowInfo.isSpectral && rowInfo.ls5Bits - 1 == bandNo) { //is it Spectral Data and the correct Band?
734  if (rowInfo.isDark) { //is it dark data?
735  if (averageDarkPixels != 0 && !darkStored) { //is there a buffer and have we not stored it already?
736  if (rowInfo.width + 2 <= averageDarkPixelLength ) { //will it fit?
737  pixelPos = 0; //only one row of average darks, at the start of the passed buffer
738  pPix = rowInfo.pPixels; //get the pointer to the stream's pixel data
739  // printf( "Dark Data: %d Saving Time Stamp\n", streamPos);
740  pixels[pixelPos++] = (uint16_t)(rowInfo.timeStamp >> 16); //save the time code ms 16-bits
741  pixels[pixelPos++] = (uint16_t)(rowInfo.timeStamp); // and ls 16-bits
742  if (blockType == EBT_UNCOMPRESSED || rowInfo.dataLen == //is the data uncompressed
743  2 * rowInfo.width) { //or stored as uncompressed?
744  for (i = 0; i < rowInfo.width; i++) { // if so then for every pixel
745  pix = *pPix++ << 8; // get the ms byte
746  pix += *pPix++; // and ls byte
747  averageDarkPixels[i + pixelPos] = pix; // and save the pixel value
748  }
749  }
750  else {
751  printf( "Dark Data Uncompressing\n");
752  UncompressKLIPixels(averageDarkPixels + pixelPos, pPix, rowInfo); //so uncompress it
753  }
754  darkStored = 1;
755  } // pixels would fit in buffer
756  else
757  break;
758  }
759  }
760  else {
761  // Non-dark data
762  //printf("row number: %d\n", rowInfo.rowNumber);
763  pixelPos = rowInfo.rowNumber * (scannedStreamInfo.spectralInfo[bandNo].width + 2);
764  if (pixelPos + rowInfo.width + 2 <= pixelLength) { //is there room for the whole row?
765  pPix = rowInfo.pPixels; //get the pointer to the stream's pixel data
766  // printf( "%d Saving Time Stamp\n", streamPos);
767  pixels[pixelPos++] = (uint16_t)(rowInfo.timeStamp >> 16); //save the time code ms 16-bits
768  pixels[pixelPos++] = (uint16_t)(rowInfo.timeStamp); // and ls 16-bits
769  if (blockType == EBT_UNCOMPRESSED || rowInfo.dataLen == 2 * rowInfo.width) {// is the data uncompressed
770  for (i = 0; i < rowInfo.width; i++) { // if so then for every pixel
771  pix = *pPix++ << 8; // get the ms byte
772  pix += *pPix++; // and ls byte
773  pixels[i + pixelPos] = pix; // and save the pixel value
774  }
775  }
776  else {
777  //otherwise the data is compressed
778  // printf( "Uncompressing\n");
779  UncompressKLIPixels(pixels + pixelPos, pPix, rowInfo); //so uncompress it
780  }
781  } // pixels would fit into buffer
782  else
783  break;
784  }
785  } // is the correct band
786  } // row scanned OK
787  } // block is pixel data
788  streamPos = headerPos = nextPos; //go the the next header
789  if (blockType == EBT_EOF) //did we find an EOF
790  break; //if so we are done
791  }
792  return res;
793 }
794 
795 /*
796  HawkeyeDecodeFinderscopeImage:
797 
798  Decode and save an image from the Finderscope a an
799  array of unsigend shorts into the passed buffer.
800 */
801 HAWKEYE_STREAM_ERROR HawkeyeDecodeFinderscopeImage(uint16_t imageNo, uint16_t *pixels, uint32_t pixelLength)
802 {
804  uint32_t streamPos;
805  uint32_t headerPos, nextPos;
806  uint32_t blockLen;
807  ENCODED_BLOCK_TYPE blockType;
808  RowInfo rowInfo;
809  uint32_t pixelPos = 0;
810  int i;
811  uint16_t pix;
812  uint8_t *pPix;
813  int fsiNo;
814  uint32_t bits, pv;
815  int noBits;
816  HeaderInfo header;
817 
818  memset(pixels, 0, 2 * pixelLength); //xero the results
819  streamPos = finderscopeOffsets[imageNo]; //start at the previously found location
820  headerPos = FindHeader(scannedStream, scannedStreamInfo.streamLength, streamPos, &header); //and fid the first record header
821  while (streamPos < scannedStreamInfo.streamLength) { //while there's bytes left to process
822  blockType = header.blockType; //remember the block type
823  blockLen = header.blockLen; //and length
824  streamPos = headerPos + sizeof(HAWKEYE_HEADER_START)+HEADER_DATA_LEN; //skip passed this header
825  nextPos = FindHeader(scannedStream, scannedStreamInfo.streamLength, streamPos, &header); //and find the next
826  if (nextPos == 0) {
827  if (headerPos == FindHeader(scannedStream, scannedStreamInfo.streamLength, finderscopeOffsets[imageNo], &header)) {
828  printf("No FinderScope Header Found.\n"); // only when the loop has not found any data yet
829  return(HSE_NO_HEADER_FOUND); // LH, 4/19/2022, l1agen_hawkeye.cpp, v1.0.4
830  } else {
831  return res;
832  }
833  }
834  if (nextPos - headerPos != blockLen ) { //is the next in the right position?
835  streamPos = headerPos = nextPos; // if not ship this record
836  continue;
837  }
838  blockLen -= 9 + sizeof(HAWKEYE_HEADER_START); //account for the record header
839  if (blockType == EBT_UNCOMPRESSED || blockType == EBT_COMPRESSED) { //did we find Pixel data?
840  if (ScanRow(scannedStream, blockLen, streamPos, &rowInfo) == 0) { //if so scan the record
841  if (!rowInfo.isSpectral) { //is it Finderscope data
842  fsiNo = rowInfo.ls5Bits - 1; //decode the image number
843  if (fsiNo == imageNo) { //is that the one we want?
844  pixelPos = rowInfo.rowNumber * scannedStreamInfo.finderscopeInfo[imageNo].width;
845  if (pixelPos + rowInfo.width <= pixelLength) { //is there room for al the pixels?
846  pPix = rowInfo.pPixels; //get a poiner to the record's pixel data
847  if (blockType == EBT_UNCOMPRESSED) { //is it uncompressed data
848  for (i = 0; i < rowInfo.width; i++) { // if so then for each pixel
849  pix = *pPix++ << 8; // get the ms 8-bits
850  pix += *pPix++; // and the ls 8-bitd
851  pixels[i+pixelPos] = pix; // and save the pixel value
852  }
853  } else { //otherwise the pixel data is packed
854  bits = 0;
855  noBits = 0;
856  for (i = 0; i < rowInfo.width; i++) { //for each pixel in the row
857  while (noBits < 10) { //we need at least 10-bits
858  pv = *pPix++; // if we dont have it
859  bits = bits | (pv << (24 - noBits)); // then get 8 more
860  noBits += 8; // and account for it
861  }
862  pix = (bits >> 22); //extract the 10-bit pixel value
863  bits = bits << 10; //keep the others
864  noBits -= 10; //account for it
865  pixels[i + pixelPos] = pix; //and save the pixel value
866  }
867  } // compressed data
868  } // pixels would fit into buffer
869  else
870  break;
871  } // is the correct image no
872  } // is finderscope data
873  } // row scanned OK
874  } // block is pixel data
875  streamPos = headerPos = nextPos; //go to the next record header
876  if (blockType == EBT_EOF) //did we find an EOF?
877  break; //if so we're done
878  }
879  return res;
880 }
uint16_t spectralBinning
Definition: HawkeyeDecode.h:53
HawkeyeImageInfo imageInfo
Definition: HawkeyeDecode.h:82
@ EBT_EOF
Definition: HawkeyeDecode.h:24
HAWKEYE_STREAM_ERROR HawkeyeScanStream(uint8_t *stream, uint32_t streamLength, HawkeyeStreamInfo *streamInfo)
int j
Definition: decode_rs.h:73
uint32_t timeStamp
Definition: HawkeyeDecode.c:35
uint16_t channelInterp
Definition: Hawkeye.h:157
HAWKEYE_STREAM_ERROR HawkeyeDecodeSpectralImage(uint16_t bandNo, uint16_t *pixels, uint32_t pixelLength, uint16_t *averageDarkPixels, uint16_t averageDarkPixelLength)
uint32_t noBadRecords
Definition: HawkeyeDecode.h:80
uint16_t channelBitfield
Definition: HawkeyeDecode.h:55
uint32_t recordNo
Definition: HawkeyeDecode.h:27
uint16_t errorCode
Definition: HawkeyeDecode.h:47
uint16_t ccd1Exposure
Definition: HawkeyeDecode.h:56
uint32_t noUnknownRecords
Definition: HawkeyeDecode.h:78
uint16_t finderscopeExposure
Definition: HawkeyeDecode.h:64
uint16_t darkSubtracted
Definition: HawkeyeDecode.h:67
@ RO_BLUE_FIRST
Definition: Hawkeye.h:102
double electronicGain
Definition: HawkeyeDecode.h:37
uint16_t shutterSolenoid
Definition: HawkeyeDecode.h:68
PARAM_TYPE_NONE Default value No parameter is buried in the product name name_prefix is case insensitive string compared to the product name PARAM_TYPE_VIS_WAVE The visible wavelength bands from the sensor are buried in the product name The product name is compared by appending and name_suffix ie aph_412_giop where prod_ix will be set to PARAM_TYPE_IR_WAVE same search method as PARAM_TYPE_VIS_WAVE except only wavelength above are looped through but prod_ix is still based ie aph_2_giop for the second band
uint16_t ccd4Exposure
Definition: HawkeyeDecode.h:59
#define MAX_FINDERSCOPE_IMAGES
Definition: HawkeyeDecode.h:18
#define MAX_TELEMETRY_RECORDS
Definition: HawkeyeDecode.h:19
uint16_t darkHeight
Definition: HawkeyeDecode.h:61
float32 * pos
Definition: l1_czcs_hdf.c:35
HAWKEYE_STREAM_ERROR HawkeyeDecodeFinderscopeImage(uint16_t imageNo, uint16_t *pixels, uint32_t pixelLength)
@ EBT_COMPRESSED
Definition: HawkeyeDecode.h:24
uint16_t knee
Definition: HawkeyeDecode.c:42
ENCODED_BLOCK_TYPE blockType
Definition: HawkeyeDecode.h:28
#define FINDERSCOPE_MAX_IMAGES
Definition: Hawkeye.h:28
uint16_t ls5Bits
Definition: HawkeyeDecode.c:32
@ RO_GREEN_FIRST
Definition: Hawkeye.h:102
uint16_t ccd3Exposure
Definition: HawkeyeDecode.h:58
uint16_t slope2
Definition: HawkeyeDecode.c:41
TelemetryPair channel[TC_NO_CHANNELS]
Definition: Hawkeye.h:161
character(len=1000) if
Definition: names.f90:13
uint16_t channelValue
Definition: Hawkeye.h:156
@ EBT_UNCOMPRESSED
Definition: HawkeyeDecode.h:24
uint32_t streamLength
Definition: HawkeyeDecode.h:76
@ DC_PACKED
Definition: Hawkeye.h:99
@ HSE_NO_IMAGE_PARAMETERS
Definition: HawkeyeDecode.h:22
uint32_t exposureID
Definition: HawkeyeDecode.h:48
char missionLog[MISSION_LOG_LENGTH]
Definition: HawkeyeDecode.h:89
uint16_t oversampling
Definition: HawkeyeDecode.h:63
@ HSE_NO_HEADER_FOUND
Definition: HawkeyeDecode.h:22
uint8_t * pPixels
Definition: HawkeyeDecode.c:43
uint16_t width
Definition: HawkeyeDecode.c:33
uint16_t slope1
Definition: HawkeyeDecode.c:41
uint32_t hawkeyeDeltaEpoch
Definition: HawkeyeDecode.h:52
uint16_t rowNumber
Definition: HawkeyeDecode.c:34
#define MISSION_LOG_LENGTH
Definition: Hawkeye.h:63
uint32_t noMissingBytes
Definition: HawkeyeDecode.h:81
uint16_t dataLen
Definition: HawkeyeDecode.c:39
HawkeyeBandInfo finderscopeInfo[MAX_FINDERSCOPE_IMAGES]
Definition: HawkeyeDecode.h:86
GetTelemetryResponse telemetry
Definition: HawkeyeDecode.h:43
uint16_t finderscopeBinning
Definition: HawkeyeDecode.h:54
@ EBT_IMAGE_PARAMS
Definition: HawkeyeDecode.h:24
uint32_t timeStamp
Definition: HawkeyeDecode.h:33
@ DC_DELTA
Definition: Hawkeye.h:99
@ TC_NO_CHANNELS
Definition: Hawkeye.h:95
uint16_t isDark
Definition: HawkeyeDecode.c:38
uint16_t blockLen
Definition: HawkeyeDecode.h:29
DATA_COMPRESSION compression
Definition: HawkeyeDecode.h:66
@ EBT_MISSION_LOG
Definition: HawkeyeDecode.h:24
@ EBT_TELEMETRY
Definition: HawkeyeDecode.h:24
uint16_t readoutOrder
Definition: HawkeyeDecode.h:69
HAWKEYE_STREAM_ERROR
Definition: HawkeyeDecode.h:22
uint32_t hostDeltaEpoch
Definition: HawkeyeDecode.h:51
@ HSE_NO_ERROR
Definition: HawkeyeDecode.h:22
uint32_t noMissingRecords
Definition: HawkeyeDecode.h:79
uint16_t ccd2Exposure
Definition: HawkeyeDecode.h:57
uint32_t FindHeader(uint8_t *stream, uint32_t streamLength, uint32_t startPos, HeaderInfo *header)
Definition: HawkeyeDecode.c:87
uint16_t noDarksAtStart
Definition: HawkeyeDecode.c:36
uint16_t isSpectral
Definition: HawkeyeDecode.c:37
uint16_t noFinderscopeImages
Definition: HawkeyeDecode.h:65
HawkeyeBandInfo spectralInfo[8]
Definition: HawkeyeDecode.h:84
@ DC_UNCOMPRESSED
Definition: Hawkeye.h:99
int i
Definition: decode_rs.h:71
uint16_t electronicGain
Definition: HawkeyeDecode.c:40
HawkeyeTelemetryInfo telemetryInfo[MAX_TELEMETRY_RECORDS]
Definition: HawkeyeDecode.h:88
ENCODED_BLOCK_TYPE
Definition: HawkeyeDecode.h:24
#define HEADER_DATA_LEN
Definition: HawkeyeDecode.c:21