/*-------------------------------------------------------------------*/ /* LAMP-LIMA: Process raw LRO telemetry files - project 15-11239.02 - SwRI */ /* */ /* $RCSfile: Acquire.c,v $ - $Revision: 1.0 $ - $Date: 2009/02/19 21:58:21 $*/ /*---------------------------------------------------------------------------*/ /* Modification history is at bottom of file DESCRIPTION This file contains all the engineering calibration information used to convert raw telemetry values specified in counts into engineering values in engineering units in the LAMP-LIMA processing. This information can be compiled into the program since these housekeeping calibration factors have been determined during ground calibration and are very unlikely to be changed. The calibration curves for all the LAMP housekeeping value are specified as polynomial approximations with factors up to a 5th order polynomial. This has proven to be more than accurate enough for the nominal values that are reported from the sensors. The program execution time may be sped up by pre-calculating the engineering values for each of the 256 possible input values for each calibration curve in advance but this has not been found necessary. This module also contains three conversion functions that conver the raw values fo the LAMP related LRO telemetry into engineering values (temperature, voltage and current). These are implemented as straight functions as the temperature conversion uses a complex logarithmic conversion and needs a reference temperature as a second input. Current and voltagre conversions could have used the polynomial scheme used for the LAMP parameters but this was not done. ------------------------------------------------------------------------------- MODIFICATION HISTORY $Author: mversteeg $ $State: Exp $ ------------------------------------------------------------------------------- $Log: Acquire.c,v $ -----------------------------------------------------------------------------*/ /* includes -----------------------------------------------------------------*/ /* defines ------------------------------------------------------------------*/ #define CALFILENAME __FILE__ #define CALFILEDATE __DATE__ __TIME__ //__TIMESTAMP__ /* typedefs -----------------------------------------------------------------*/ /* globals ------------------------------------------------------------------*/ // definitions for all enumerated types, matching range/value of HK entries const char OpStatStr[8][8] = {"undef","check","safe","acq-pix" ,"undef","check","safe","acq-his"}; const char SafetyStr[2][8] = {"none","safety"}; const char LastSafetyStr[8][12]= {"nosafety","Countrate","McpV","StripI" ,"AnodeV","HvCycle","Temperature","incorrect7"}; const char ABBothStr[4][8] = {"none","b","a","both"}; const char TurnOffStr[2][10]= {"run","turnoff"}; const char OffOnStr[2][5]= {"off","on"}; const char ActiveStr[2][10]= {"inactive","active"}; const char PendingStr[2][8]= {"none","pending"}; const char DumpStr[2][8]= {"none","allowed"}; const char TcStatStr[8][12]= {"0","1st-either","nth-pri","nth-red" ,"1st-pri","1st-red","6","7"}; // CommandStr() is a function() const char AcqStr[2][8]= {"pixel","histo"}; const char ABStr[2][3]= {"A","B"}; const char LastStr[2][8]= {"inter","last"}; const char HwCtrlStr[2][8]= {"sw","hw"}; const char DetDoorStr[4][9] = {"illegal","open","not-open","illegal3"}; const char ApDoorStr[4][8] = {"error","closed","open","between"}; const char LtsDarkStr[2][8] = {"day","night"}; const char OverflowStr[2][10]= {"none","overflow"}; const char StimStr[2][8]= {"no-stim","stim"}; const char ReqStr[4][10]= {"none","light","dark","dark"}; const char OverrideStr[2][10]= {"none","override"}; const char MaskedStr[2][8]= {"enable","masked"}; const char CodeStr[16][10]= {"0","1","2","3","PROM","PROM","PROM","PROM" ,"EEPROM_1","EEPROM_2","EEPROM_3","EEPROM_4" ,"12","13","14","15"}; const char HwStr[16][8]= {"0","L1","L2","FM","L3","SIM","6","PROM" ,"8","9","10","11","12","13","14","15"}; const char SlowStr[8][10]= {"start","idle","mem-check","mem-dump" ,"mem-load","histogram","pixellist","test"}; // definitions for all enumerated types used for S/C enumerated types const char ScOffOnStr[2][5]= {"off","on"}; const char ScErrorStr[2][5]= {"ok","fail"}; const char ScEnableStr[2][8]= {"disable","enable"}; const char ScOpenStr[2][7]= {"closed","open"}; // definitions of all LAMP engineering conversions double CALPSETV [MAXCALTABELLEN][2] = {{ 0, 0.0} ,{ 1, -0.061694499999999999} ,{ 2, 0.00080768300000000001} ,{ 3, -8.3281299999999995e-006} ,{ 4, 4.0910900000000003e-008} ,{ 5, -7.6895599999999994e-011} ,{-1, 0.0}}; double CALPMCPV [MAXCALTABELLEN][2] = {{ 0, 0.0} ,{ 1, -0.0683613} ,{ 2, 0.00086269799999999996} ,{ 3, -8.4984899999999993e-006} ,{ 4, 3.9582999999999998e-008} ,{ 5, -7.1166099999999996e-011} ,{-1, 0.0}}; double CALPSTPI [MAXCALTABELLEN][2] = {{ 0, 0.0 } ,{ 1, 0.118214688525} ,{-1, 0.0 }}; double CALPSUMI [MAXCALTABELLEN][2] = {{ 0, 0.0 } ,{ 1, 0.09304640} ,{-1, 0.0 }}; double CALPANOV [MAXCALTABELLEN][2] = {{ 0, 0.0 } ,{ 1,-3.16804} ,{-1, 0.0 }}; double CALPDISC [MAXCALTABELLEN][2] = {{ 0, 0.0 } ,{ 1, 0.01176470588235} ,{-1, 0.0 }}; double CALTEMP [MAXCALTABELLEN][2] = {{ 0,-78.030000000000001} ,{ 1, 2.3849999999999998} ,{ 2, -0.040869999999999997} ,{ 3, 0.00037520000000000001} ,{ 4, -1.601e-006} ,{ 5, 2.5939999999999999e-009} ,{-1, 0.0}}; /* locals -------------------------------------------------------------------*/ /* forward declarations -----------------------------------------------------*/ /*-----------------------------------------------------------------------*/ double HvSetCal(int i) /* ARGS raw HvSet value in counts (0..255) RETURNS enginering value for HvSet in kVolt */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPSETV[n][1]*pow(i,CALPSETV[n][0]); n++; } return(res); } /* HvSetCal() */ /*-----------------------------------------------------------------------*/ double McpVCal(int i) /* ARGS raw McpV value in counts (0..255) RETURNS engineering value of McpV in kVolt */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPMCPV[n][1]*pow(i,CALPMCPV[n][0]); n++; } return(res); } /* McpVCal() */ /*-----------------------------------------------------------------------*/ double AnoVCal(int i) /* ARGS raw AnodeV value in counts (0..255) RETURNS engineering value of AnodeV in Volt */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPANOV[n][1]*pow(i,CALPANOV[n][0]); n++; } return(res); } /* AnoVCal() */ /*-----------------------------------------------------------------------*/ double StpICal(int i) /* ARGS raw StripI value in counts (0..255) RETURNS engineering value of StripI in microAmp */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPSTPI[n][1]*pow(i,CALPSTPI[n][0]); n++; } return(res); } /* StpICal() */ /*-----------------------------------------------------------------------*/ double SumICal(int i) /* ARGS raw SumStripI value in counts (0..255) RETURNS engineering value of total SumStripI in microAmp */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPSUMI[n][1]*pow(i,CALPSUMI[n][0]); n++; } return(res); } /* SumICal() */ /*-----------------------------------------------------------------------*/ double DiscrCal(int i) /* ARGS raw discriminator level in counts (0..255) RETURNS engineering value of discriminator voltage in Volt */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALPDISC[n][1]*pow(i,CALPDISC[n][0]); n++; } return(res); } /* DiscrCal() */ /*-----------------------------------------------------------------------*/ double TempsCal(int i) /* ARGS raw temperature value in counts (0..255) RETURNS engineering value of temperature in degrees Celsius */ { int n=0; double res= 0.0; while ((n=0)) { res+= CALTEMP[n][1]*pow(i,CALTEMP[n][0]); n++; } return(res); } /* TempsCal() */ /*-----------------------------------------------------------------------*/ double ScTempsCal(int t, int r) /* ARGS raw s/c temperature value, raw reference temperature value RETURNS engineering value of temperature in degrees Celcius */ { return((1/(0.0014733+(0.0002372*log(((2008.98*t-47512) /(1.00449*r-0.49788*t-11.981)))) +0.0000001074*pow(log(((2008.98*t-47512) /(1.00449*r-0.49788*t-11.981))),3)))-273.15); } /* ScTempsCal() */ /*-----------------------------------------------------------------------*/ double ScCurrentCal(int c) /* ARGS raw s/c current value RETURNS engineering value of current in Amps */ { return((0.00500220*c)-10.23126758); } /* ScCurrentCal() */ /*-----------------------------------------------------------------------*/ double ScVoltageCal(int v) /* ARGS raw s/c voltage value RETURNS engineering value voltage in Volt */ { return((0.02059008*v)-42.13030650); } /* ScVoltageCal() */ /*---- end of file ----------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* LAMP-LIMA: Process raw LRO telemetry files - project 15-11239.02 - SwRI */ /* */ /* $RCSfile: Acquire.c,v $ - $Revision: 1.0 $ - $Date: 2009/02/19 21:58:21 $*/ /*---------------------------------------------------------------------------*/ /* Modification history is at bottom of file DESCRIPTION This file contains the main output generating functionality ofthe LAMP-LIMA program. It generates the FITS file that contains all the information from the input file(s). ------------------------------------------------------------------------------- MODIFICATION HISTORY $Author: mversteeg $ $State: Exp $ ------------------------------------------------------------------------------- $Log: Acquire.c,v $ -----------------------------------------------------------------------------*/ /* includes -----------------------------------------------------------------*/ /* defines ------------------------------------------------------------------*/ /* typedefs -----------------------------------------------------------------*/ /* globals ------------------------------------------------------------------*/ /* locals -------------------------------------------------------------------*/ /* forward declarations -----------------------------------------------------*/ /*-----------------------------------------------------------------------*/ double average(double sum,double n) /* ARGS total summed value number to average over RETURNS average value is the number to average over is not too small (-1 otherwise) */ { if (n----------------------------------------------------------------------*/ unsigned int FormInt(int n, BYTE byt[]) /* ARGS number of bytes, array of bytes RETURNS concatenated integer value DESCRIPTION Concatenate a number of bytes together to form an integer number, used to convert from BYTE arrays into 2, 3 and 4 BYTE integers. */ { int i; int value=0; for (i=0;i----------------------------------------------------------------------*/ void WriteFitsLine(char verbose, FILE *fp, char key[], char val[], char desc[]) /* ARGS verbose status, output file pointer, keyword string, value string, descriptor string RETURNS none DESCRIPTION Write one properly formatted 80-character KEYWORD line to a FITS file, consisting of keyword, value and description. The fields are truncated to the appropriate lengths to FITS within the 80-column limit. Note that the keyword is NOT converted to upper case so the caller needs to take care of this, and that string values need to be properly quoted. The function will print warnings if incorrectly formatted fields are supplied, but since this function is called internally in LAMP-LIMA these should be considered internal (coding) errors. If the verbose indicator is 2 or larger then the resulting string will also be send to standard output. */ { char line[82]= ""; if (strlen(key)>8) { printf("WARNING: inproper FITS keyWORD length: %s\n",key); key[8]= '\0'; } if (strlen(key)+strlen(val)+5>80) /* limit value length to 67 chars */ { printf("ERROR: value field too large: %s\n",val); } else { if (strcmp(key,"")==0) { if (strcmp(val,"")!=0) printf("WARNING: value discarded: %s\n",val); sprintf(line,"%30s / ",""); } else if (strcmp(key,"END")==0) { if ((strcmp(val,"")!=0)||(strcmp(desc,"")!=0)) printf("WARNING: value and/or desc discarded: %s - %s\n",val,desc); sprintf(line,"%s",key); } else if ((strcmp(key,"COMMENT")==0)||(strcmp(key,"HISTORY")==0)) { if (strcmp(val,"")!=0) printf("WARNING: value discarded: %s\n",val); sprintf(line,"%-8s",key); } else if (val[0]=='\'') { sprintf(line,"%-8s= %-20s / ",key,val); } else { sprintf(line,"%-8s= %20s / ",key,val); } strncat(line,desc,80-strlen(line)); fprintf(fp,"%-80s",line); if (verbose>1) { printf("%s\n",line); } } } /* WriteFitsLine() */ /*-----------------------------------------------------------------------*/ void WriteFitsBYTE(FILE *fp, BYTE b) /* ARGS output file pointer, byte value RETURNS none DESCRIPTION Write one BYTE to the FITS file in a binary section. */ { fputc(b,fp); } /* WriteFitsBYTE() */ /*-----------------------------------------------------------------------*/ void WriteFitsLONG(FILE *fp, LONG lng) /* ARGS output file pointer, value (32 bit long) RETURNS none DESCRIPTION Write one LONG (unsigned 32 bit value) to the FITS file in a binary section as four separate bytes. */ { fputc(((lng)>>24)&0xff,fp); fputc(((lng)>>16)&0xff,fp); fputc(((lng)>> 8)&0xff,fp); fputc(((lng) )&0xff,fp); } /* WriteFitsLONG() */ /*-----------------------------------------------------------------------*/ void WriteFitsWORD(FILE *fp, WORD wrd) /* ARGS output file pointer, value (16 bit word) RETURNS none DESCRIPTION Write one 16 bit WORD (unsigned 16 bit value) to the FITS file in a binary section. Note: FITS only supports unsigned formats through a kludge involving double application of an offset, therefore this write assumes that the proper offset and scaling have been specified in the segment header: BZERO=32768 and BSCALE=1 */ { fputc((wrd>>8)&0xff,fp); fputc((wrd )&0xff,fp); } /* WriteFitsWORD() */ /*-----------------------------------------------------------------------*/ void WriteFitsFLOAT(FILE *fp, float f) /* ARGS output file pointer, value (float) RETURNS none DESCRIPTION Write one 32 bit float to the FITS file in a binary section, the function uses the program internal representation of a 32-bit float to be written to the output file as this matches the FITS file binary representation of a 32-bit float number (IEEE float). If this program were to run on a system where this wasn't the case then a more elaborate conversion would have to be performed. */ { union { BYTE ba[4]; float ff; } conv; int i; conv.ff= f; for (i=3;i>=0;i--) // 0x42F6E979 = 123.456 { fputc(conv.ba[i],fp); } } /* WriteFitsFLOAT() */ /*-----------------------------------------------------------------------*/ void WriteFitsDOUBLE(FILE *fp, double d) /* ARGS output file pointer, value (double) RETURNS none DESCRIPTION Write one 64 bit double to the FITS file in a binary section, the function uses the program internal representation of a 64-bit float to be written to the output file as this matches the FITS file binary representation of a 64-bit float number (IEEE double). If this program were to run on a system where this wasn't the case then a more elaborate conversion would have to be performed. */ { union { BYTE ba[8]; double dd; } conv; int i; conv.dd= d; for (i=7;i>=0;i--) // 0x41A75E2BCC85A1CB = 196023782.261 { fputc(conv.ba[i],fp); } } /* WriteFitsDOUBLE() */ /*-----------------------------------------------------------------------*/ void Fill2880(char verbose, FILE *fp, char ch) /* ARGS verbose status, output file pointer, fill character RETURNS none DESCRIPTION Pad a generated FITS segment to a valid multiple of 2880, by writing the specified pad character untill the file size is a multiple of 2880. If the verbose level is set above 5 then the function will report how many pad bytes have to be written. */ { int i=0; if (verbose>5) { printf("fill to multiple with 0x%02x, by %d\n",ch,ftell(fp)%2880); } while ((ftell(fp)%2880)!=0) { fputc(ch,fp); i++; } } /* Fill2880() */ /*-----------------------------------------------------------------------*/ char *Int2Str(int i) /* ARGS integer value, RETURNS pointer to character string DESCRIPTION Convert an integer number into a (decimal) string representation, and return a pointer to this string. The string itself is kept in a static internal storage, so the function is non-reentrant, and the caller has to ensure that the function is only called once before the result is being used. The internal string storage is limited to 20 characters which 'limits' the input value to 9999999999999999999 (>2^64), the effect is unpredictable if a larger number is supplied. */ { static char str[20]; sprintf(str,"%d",i); return(str); } /* Int2Str() */ /*-----------------------------------------------------------------------*/ char *Hex2Str(int i) /* ARGS integer value, RETURNS pointer to character string DESCRIPTION Convert an integer number into a (hexadecimal) string representation, and return a pointer to this string, the string itself is kept in a static internal storage, so the function is non-reentrant, and the caller has to ensure that the function is only called once before the result is being used. The output value is preceded by '0x' and is at least four bytes long. The internal string storage is limited to 20 characters which 'limits' the input value to 0xfffffffffffffffff (2^68), the effect is unpredictable if a larger number is supplied. */ { static char str[20]; sprintf(str,"0x%04x",i); return(str); } /* Int2Str() */ /*-----------------------------------------------------------------------*/ char *Flt2Str(double a, int width, int precision) /* ARGS double value, RETURNS pointer to character string DESCRIPTION Convert a double number into a fixed point string representation using the specified number of fractional digits, and return a pointer to this string, the string itself is kept in a static internal storage, so the function is non-reentrant, and the caller has to ensure that the function is only called once before the result is being used. The maximum number allowed for the width if 20 characters, if a larger number is specified a width of 20 is used. */ { static char str[25]; if (width<20) { sprintf(str,"%*.*f",width,precision,a); } else { sprintf(str,"%20.*f",precision,a); } return(str); } /* Flt2Str() */ /*-----------------------------------------------------------------------*/ char *QStr(char str[]) /* ARGS string, RETURNS pointer to quoted sting DESCRIPTION Quote the input string with single quotes and return a pointer to the resulting string. The buffer needed for this is kept in an internal static variable which limits the length of the string to be quoted to a maximum of 80 characters and also makes the function non-reentrant so the caller has to ensure that the result is used before the function is called again. */ { static char qstr[83]; if (strlen(str)>80) { printf("WARNING: quoted string truncated\n"); } sprintf(qstr,"'%0.80s'",str); return(qstr); } /* QStr() */ /*-----------------------------------------------------------------------*/ char *NumiStr(char key[], unsigned i) /* ARGS input string, RETURNS pointer to unique string DESCRIPTION Make a unique string by replacing the last character(s) with a minimal number of (up to 3) digits and return a pointer to the resulting string. The buffer needed for this is kept in an internal static variable which limits the length of the string to be quoted to a maximum of 20 and also makes the function non-reentrant so the caller has to ensure that the result is used before the function is called again. */ { static char str[21]; if (strlen(str)>=20) { printf("WARNING: numbered string truncated\n"); } strcpy(str,key); if ((i>0)&&(strlen(str)>0)) { str[strlen(str)-1]= '0'+(i%10); if ((i>=10)&&(strlen(str)>1)) { str[strlen(str)-2]= '0'+((i/10)%10); if ((i>=100)&&(strlen(str)>2)) { str[strlen(str)-3]= '0'+((i/100)%10); } } } return(str); } /* NumiStr() */ /*================================================================================*/ /*================================================================================*/ /*================================================================================*/ char HwVerStr[16][8] = {"x0","liis-1","liis-2","fm","liis-3","si","6","pr" ,"x8","tsim","xA","xB","xC","xD","xE","xF"}; char SafeStatStr[8][8]= {"undef","none","active","undef3","undef4","var"}; char LastSafeStr[8][8]= {"none","bright","mcpv","stripi","anodev","cycle" ,"temp","undef7"}; char ApDoorStatStr[6][8] = {"undef","error","close","open","between","var"}; char DetDoorStatStr[6][8] = {"undef","undef1","open","close","undef4","var"}; char HtrStatStr [6][8] = {"undef","off","b","a","both","var"}; char LtsStatStr[6][8] = {"undef","day","night","undef3","undef4","var"}; char PowerStatStr[6][8] = {"undef","none","b","a","both","var"}; char StimStatStr[8][8]= {"undef","off","stim","undef3","undef4","var"}; char LogicStr[2][2] = {"F","T"}; char HisGndStr[2][8] = {"ground","histo"}; char TestPatternStr[9][8] = {"data","inc-hi","inc-lo","dec-hi","dec-lo","const" ,"histo","hists","pixel"}; char FrmDataStr[2][2][8] = {{"none","pixel"},{"histo","mixed"}}; /*-----------------------------------------------------------------------*/ void WriteFitsImage(char verbose, FILE *fp, IMAGE im, char str[]) /* ARGS verbose status, output file pointer, image (32768 long values) RETURNS none DESCRIPTION Write one image consisting of 32768 32-bit words to a binary section of a FITS file and pad the section with the appropriate number of zero pad bytes to satisfy the FITS formatting rules for a binary section. If verbose is larger than 4 a report of this action is also printed. */ { int i; if (verbose>4) { printf("#### %s\n",str); } for (i=0;i<32768;i++) WriteFitsLONG(fp,im[i]); Fill2880(verbose,fp,'\0'); } /* WriteFitsImage() */ /*-----------------------------------------------------------------------*/ void WriteFitsAcquisitionListExt2(char verbose,FILE *fp ,ACQLIST *acq , HACKTIMEFRAME htf[MAXTIMEFRAMES]) /* ARGS verbose status, acquisition list, hack time correlation list RETURNS none DESCRIPTION Write the complete acquisition list to an ASCII table entry of the FITS file, it is assumed that the appropriate header has already been written and this function will just write the matching table section with the FITS required padding with a space character. In the LAMP-LIMA program this will be FITS extension 3. If verbose is larger than 2 the fact that the acquisition list is generated will be acknowledged, if verbose is larger than 3 the complete list will also be printed to standard output. */ { int i; double epoch; char line[100]; const char HVPSNUM[8]= {0,1,3,2,4,5,9,9}; // translate from inverted numbering if (verbose>2) { printf("#### EXTENSION 2: ASCII TABLE: Acquisition List, %3d entrie(s)\n" ,acq->acquisitions); } for (i=0;iacquisitions;i++) { if (acq->ac[i].scihdr!=NOHKHEADER) { sprintf(line,"%14.3f %14.3f %5d %1d %4d %1d %1d %1d %1d %1d %2d %8lu" ,reconstructedSUTC(acq->ac[i].hackstart,htf) ,reconstructedSUTC(acq->ac[i].hackstop,htf) ,acq->ac[i].scihdr ,(acq->ac[i].header>>15)&1 ,acq->ac[i].frameno ,acq->ac[i].apdstate&7 ,acq->ac[i].ltsstate&7 ,acq->ac[i].stimstate&7 ,HVPSNUM[acq->ac[i].hvpsstate&7] ,acq->ac[i].hackrate&7 ,scienceType(acq->ac[i].checksum)&63 ,acq->ac[i].checksum); } else { // unmatched entry no HK reference available sprintf(line,"%14.3f %14.3f %5d %1d %4d %1d %1d %1d %1d %1d %2d %8lu" ,reconstructedSUTC(acq->ac[i].hackstart,htf) ,reconstructedSUTC(acq->ac[i].hackstop,htf) ,acq->ac[i].header ,(acq->ac[i].header>>15)&1 ,acq->ac[i].header&0xfff ,5 ,5 ,5 ,5 ,acq->ac[i].hackrate&7 ,scienceType(acq->ac[i].checksum)&63 ,acq->ac[i].checksum); } fprintf(fp,line); if (verbose>1) { printf("## %3d|%s|\n",i+1,line); } } if (verbose>3) { epoch= 10000.0*((((LONG)acq->ac[0].start)/10000)-1); printf("# acq timing: epoch %12.3f\n",epoch); for (i=0;iacquisitions;i++) { printf("#%2d %9.3f %9.3f %8.3f %06lx %06lx %9.3f %9.3f %8.3f\n" ,i+1 ,acq->ac[i].start-epoch ,acq->ac[i].stop-epoch ,acq->ac[i].stop-acq->ac[i].start ,acq->ac[i].hackstart ,acq->ac[i].hackstop ,reconstructedSUTC(acq->ac[i].hackstart,htf)-epoch ,reconstructedSUTC(acq->ac[i].hackstop,htf)-epoch ,(reconstructedSUTC(acq->ac[i].hackstop,htf ) -reconstructedSUTC(acq->ac[i].hackstart,htf))); } } Fill2880(verbose,fp,' '); } /* WriteFitsAcquisitionListExt2() */ /*-----------------------------------------------------------------------*/ void WriteFitsFramesExt3(char verbose, char correct, FILE *ffp, unsigned char sfil[] ,FILEHEADER file[], int frames, LONG hackfixes) /* ARGS verbose status, output file pointer, science files index list, list of all file headers, number of frames RETURNS none DESCRIPTION Generate the raw science frames binary table extension for the LAMP-LIMA generated FITS file, this is supposed to be extension 3 of the LAMP-LIMA FITS file. It is assumed that an appropriate header section for this extension has already been generated. Each frame entry consists of one double indicating the start time of the frame (acquisition) followed by 32768 words of 16-bit values, using the FITS required offset stored format representing 16-bit unsigned integers. To generate this extension the science file(s) are read again and the information is copied from the science file(s) into the FITs file. During this generation of the FITS extension the hack bit flip corrections are again detected and applied once more so they also end up in the generated FITs file, this is a double process but only this way the processing can be performed without reading all the complete frames in to memory first. The number of applied fixes is checked against the number of fixes found during the first pass of the science data, since the same check and fix process is used here both numbers should be identical, otherwise an internal error has been detected. The generated output is padded with zero characters to satisfy the FITS format. If verbose is larger than 2 the fact that the frame extension is generated will be acknowledged, if verbose is positive then the re-application of timehack bit-flip fixes is acknowledged. */ { unsigned si,i; FILE *ifp; FILEHEADER hdr; SUTC stim; WORD ident; WORD w; int lasthack; int hackrate; if (verbose>2) { printf("### EXTENSION 3: BINARY TABLE: Frame Data (%d frame(s))\n",frames); } for (si=0;si2) { if (!(ident&0x8000)) { printf("## pixel frame 0x%04x time %s\n",ident,utcTimeStr(stim)); } else { printf("## histo frame 0x%04x time %s\n",ident,utcTimeStr(stim)); } } lasthack= -1; hackrate= -1; WriteFitsDOUBLE(ffp,realSUTC(stim)); WriteFitsWORD(ffp,(WORD)(ident-32768)); // offset correct to store unsigned for (i=1;i<32768;i++) { readDROW(ifp,&w); if ((!(ident&0x8000))&&(w&0x8000)) { // timehack (only applicable in pixellist) if ((lasthack>=0)&&(w!=lasthack-1)) // exclude decrementing test pattern { if (hackrate<0) { // attempt to calculate hackrate once two times are available hackrate= calculateHackrate(w-lasthack); } // note the hackrate starts numbering here at 1 (= 4ms) if (hackrate>0) // once correct hackrate has been established { if ((correct)&&(((w-lasthack+0x8000)%0x8000)!=(1<<(hackrate-1)))) { // try to fix the problem (simple way!!) w= ((lasthack+(1<<(hackrate-1)))&0x7fff)|0x8000; if (verbose>0) { printf("# frame 0x%04x: reapplied fix (to 0x%04x at %d)\n" ,ident,w,i); } hackfixes--; } } } lasthack= w; } WriteFitsWORD(ffp,(WORD)(w-32768)); // offset correct to store unsigned } readLONG(ifp,&(stim.sec)); // pre-read next so file status will show eof() } } else { printf("FATAL: file too small (<64): %s\n",file[sfil[i]].realname); exit(3); } fclose(ifp); } else { printf("FATAL: cannot access input file: %s\n",file[sfil[i]].realname); exit(3); } } if (hackfixes!=0) { printf("FATAL: internal, number of applied hack fixes doesn't match (%d)\n",hackfixes); exit(3); } Fill2880(verbose,ffp,'\0'); } /* WriteFitsFramesExt3() */ /*-----------------------------------------------------------------------*/ long determineHackIntervals(char verbose, FILE *ffp, unsigned char sfil[] , FILEHEADER file[], ACQLIST *acq , HACKTIMEFRAME htf[MAXTIMEFRAMES]) /* ARGS verbose status, output file pointer, science files index list, list of all file headers, hack time correlation list, RETURNS number of countrate intervals spanned by the data DESCRIPTION Determine the number of hack intervals from the series of pixellist science files taking into account the possibility of gaps between the pixellist frames. If there is only a single continuous series of pixellist frames then this number of intervals is simply to calculate as it is one less than the number of hacks, but if there are multiple sets of continuous pixellist frames then one interval is lost for each set of continuous frames. This numbder needs to be determined before the generation fo the list of countrates as it has to be inserted into the FITS file before the actual countrates list is added. This funcion uses exactly the same algorithm as the next function which actually writes the countrate numbers to the fits file but it only counts the number of entries it would have written (without actually writing them), and returns this number as the result. If verbose is larger than 2 the fact that the countrate extension is generated will be acknowledged, if verbose is larger than 3 each processed input file name will be listed. */ { unsigned si,i; unsigned frame=0; FILE *ifp; FILEHEADER hdr; SUTC stim; char ok= 1; WORD ident,w; int lasthack= -1; int hack; int exthack; LONG bytes= 0; int acqi=0; long written= 0; if (verbose>2) { printf("#### EXTENSION 4: determine number of hack intervals from pixellists\n"); } for (si=0;si3) { printf("## Sci %s: frame 0x%04x time %s\n" ,file[sfil[si]].realname,ident,utcTimeStr(stim)); } if (((ident&0x8000)>>15)==PIXELLIST) { // pixellist exthack= acq->ac[acqi].hackstart; // adjust to start time of frame if ((acqi>0)&&(exthack!=acq->ac[acqi-1].hackstop)) { // non-consecutive pixellist frames restart lasthack= -1; } for (i=1;i<32768;i++) { readDROW(ifp,&w); if (w&0x8000) { hack= w&0x7fff; if (lasthack>=0) { exthack+= (hack-lasthack); if (hack dewrap exthack+= 0x8000; } written++; } lasthack= hack; } } } else { // histogram, no action this time skipBYTEs(ifp,65534); } readLONG(ifp,&(stim.sec)); // pre-read so file status will show eof() acqi++; } } else { printf("FATAL: file too small (<64): %s\n",file[sfil[i]].realname); exit(3); } fclose(ifp); } else { printf("FATAL: cannot access input file: %s\n",file[sfil[i]].realname); exit(3); } } return(written); } /* determineHackIntervals() */ /*-----------------------------------------------------------------------*/ void WriteFitsCountratesExt4(char verbose, FILE *ffp, unsigned char sfil[] , FILEHEADER file[], ACQLIST *acq , HACKTIMEFRAME htf[MAXTIMEFRAMES], long entries) /* ARGS verbose status, output file pointer, science files index list, list of all file headers, hack time correlation list, number of countrate entries RETURNS none DESCRIPTION Generate the countrate binary table extension for the LAMP-LIMA generated FITS file, this is supposed to be extension 4 of the LAMP-LIMA FITS file. It is assumed that an appropriate header section for this extension has already been generated. This section consists of (digital rate based) countrate values calculated based on pixellist science acquisitions, so this section only contains data if the input science files contained pixellist acquisition data, otherwise this extension is empty. In either case a coarse (analog rate based) countrate dataset is available in the housekeeping dataset which contains countrate data with a resolution of 1 Hz. To generate this extension the science file(s) are read once more and the countrates are calculate from the hack entries and the number of photon events captured in between. The hackrate selected during the acquisition determines the rate at which countrates can be calculated. The resolution of the calculated countrate depends also the hack-rate that was active during the pixellist acquisition. For instance in a pixellist acquisition run with a 4 ms hackrate the resolution of the countrate will be 250 Hz, but in an acquisition with a 16 ms hackrate the resolution will be 62.5 Hz. Each countrate entry consists of a 4-byte integer containing the extended hack time, a double showing the corresponding spacecraft time and another 4-byte integer with the calculated countrate (in Hz). The generated output is padded with zero characters to satisfy the FITS format. If verbose is larger than 2 the fact that the countrate extension is generated will be acknowledged, if verbose is larger than 3 each processed input file name will be listed. */ { unsigned si,i; unsigned frame=0; FILE *ifp; FILEHEADER hdr; SUTC stim; WORD ident,w; int lasthack= -1; WORD events=0; char ok= 1; LONG cr; int hack; int exthack; LONG bytes= 0; int acqi=0; long written= 0; if (verbose>2) { printf("### EXTENSION 4: BINARY TABLE: Calculated Countrates (%ld) from pixellists\n",entries); } for (si=0;si3) { printf("#### Sci %s: frame 0x%04x time %s\n" ,file[sfil[si]].realname,ident,utcTimeStr(stim)); } if (((ident&0x8000)>>15)==PIXELLIST) { // pixellist exthack= acq->ac[acqi].hackstart; // adjust to start time of frame if ((acqi>0)&&(exthack!=acq->ac[acqi-1].hackstop)) { // non-consecutive pixellist frames restart lasthack= -1; } for (i=1;i<32768;i++) { readDROW(ifp,&w); if (w&0x8000) { hack= w&0x7fff; if (lasthack>=0) { cr= (long)(0.5+(events/(((hack-lasthack+0x8000)%0x8000) *(reconstructedSUTC(exthack+1,htf) -reconstructedSUTC(exthack,htf )) ))); exthack+= (hack-lasthack); if (hack dewrap exthack+= 0x8000; } WriteFitsLONG(ffp,exthack); WriteFitsDOUBLE(ffp,reconstructedSUTC(exthack,htf)); WriteFitsLONG(ffp,cr); written++; } lasthack= hack; events= 0; } else { events++; } } } else { // histogram, no action this time skipBYTEs(ifp,65534); } readLONG(ifp,&(stim.sec)); // pre-read so file status will show eof() acqi++; } } else { printf("FATAL: file too small (<64): %s\n",file[sfil[i]].realname); exit(3); } fclose(ifp); } else { printf("FATAL: cannot access input file: %s\n",file[sfil[i]].realname); exit(3); } } Fill2880(verbose,ffp,'\0'); if (entries!=written) { printf("ERROR: entries written doesn't match expected number: %d<>%d\n" ,entries,written); } } /* WriteFitsCountratesExt4() */ /*-----------------------------------------------------------------------*/ void WriteFitsHighLtsExt5(char verbose, FILE *ffp,unsigned char hfil[] , FILEHEADER file[] , HACKTIMEFRAME htf[MAXTIMEFRAMES], long entries) /* ARGS verbose status, output file pointer, housekeeping files index list, list of all file headers, hack time correlation list, number of countrate entries RETURNS none DESCRIPTION Generate the high rate LTS binary table extension for the LAMP-LIMA generated FITS file, this is supposed to be extension 5 of the LAMP-LIMA FITS file. It is assumed that an appropriate header section for this extension has already been generated. The high rate LTS data is extracted from the housekeeping data so the housekeeping file(s) are read once more. Each housekeeping packet contains either 10 or 9 samples of high rate LTS data. If the debug option in the housekeeping data is set to select the high resolution LTS acquisition time (debug option 2), this time information is used otherwise the samples are assumed to be spaced equidistant within the one second housekeeping time slot. Each high rate LTS entry consists of a 4-byte integer containing the extended hack time, a double showing the corresponding spacecraft time and two byte value with the high rate LTS values. The generated output is padded with zero characters to satisfy the FITS format. If verbose is higher than 2 the fact that the HK extension is generated will be acknowledged, if verbose is higher than 3 each processed input file name will be listed. */ { unsigned hi,j,slots; FILE *ifp; FILEHEADER hdr; char ok = 1; int hkpktcnt= -1; LONG time= -1; CCSDSHEADER ccsds; SUTC stim; BYTE hkpkt[LAMPHKPKTLEN-5]; int hacktime= 0; int ltshacktime; resetHackWrap(); if (verbose>2) { printf("### EXTENSION 5: BINARY TABLE: High Rate LTS from HK (%ld entrie(s))\n",entries); } for (hi=0;hi3) { printf("#### Hk: %s, start %s\n",file[hfil[hi]].realname, utcTimeStr(file[hfil[hi]].Start)); } while ((!feof(ifp))&&(ok)) { if (readCcsdsHeader(ifp,&ccsds)) { if (ccsds.apid==LAMPHKAPID) { hkpktcnt= ccsds.pktcnt; if (ccsds.pktlen!=LAMPHKPKTLEN) { printf("ERROR: unexpected HK packet size %d\n",ccsds.pktlen); ok= 0; } readLONG(ifp,&(stim.sec)); readWORD(ifp,&(stim.sub)); readString(ifp,hkpkt,ccsds.pktlen-5); // sec & sub already read hacktime= unwrappedHackTime(hkpkt[24],hkpkt[25]); slots= validHiLtsEntries(&hkpkt[50],&hkpkt[60] ,hkpkt[104],&hkpkt[93]); for(j=0;jhkpkt[25]) { ltshacktime-= 256; } } else { /* just create high resolution time, based on nominal */ /* rate of 25 hack ticks (= 25 * 4 ms) per cycle. */ ltshacktime= hacktime-((10-j)*25); } WriteFitsLONG(ffp,ltshacktime); WriteFitsDOUBLE(ffp,reconstructedSUTC(ltshacktime,htf)); WriteFitsBYTE(ffp,hkpkt[50+j]); WriteFitsBYTE(ffp,hkpkt[60+j]); } entries-= slots; } else if (ccsds.apid=LAMPMDAPID) { skipBYTEs(ifp,ccsds.pktlen+1); } else { printf("WARNING: skipping unexpected packet ApId %d, len %d\n",ccsds.apid,ccsds.pktlen); skipBYTEs(ifp,ccsds.pktlen+1); } } else if (!feof(ifp)) { printf("ERROR: incorrect CCSDS header near %d in file %s\n",ftell(ifp),file[hfil[hi]].realname); ok= 0; } } } else { printf("FATAL: file too small (<64): %s\n",file[hfil[hi]].realname); exit(3); } fclose(ifp); } else { printf("ERROR: cannot access input file: %s\n",file[hfil[hi]].realname); exit(3); } } if (entries!=0) { printf("ERROR: internal inconsistency in LTS entries count (left %ld)\n",entries); ok= 0; } Fill2880(verbose,ffp,'\0'); } /* WriteFitsHighLtsExt5() */ /*-----------------------------------------------------------------------*/ void WriteFitsHkPacketsExt6(char verbose, FILE *ffp, unsigned char hfil[] , FILEHEADER file[] , HACKTIMEFRAME htf[MAXTIMEFRAMES], long entries) /* ARGS verbose status, output file pointer, housekeeping files index, list of all file headers, hack time correlation list, number of housekeeping packets RETURNS none DESCRIPTION Generate the housekeeping packet binary table extension for the LAMP-LIMA generated FITS file, this is supposed to be extension 6 of the LAMP-LIMA FITS file. It is assumed that an appropriate header section for this extension has already been generated. The function reads the housekeeping file(s) once more and copies the data from the housekeeping packet into the FITS file. Each housekeeping packet entry consists of a 4-byte integer containing the extended hack time, a double showing the corresponding spacecraft time, a two byte packet count and a raw section of the 122 bytes directly out of the HK packet. After this the entry contains 33 fields with either engineering values of HK packet fields or merged HK packet fields consisting of multiple bytes. So these multi byte and engineering value entries are directly accessible. The generated output is padded with zero characters to satisfy the FITS format. If verbose is higher than 2 the fact that the frame extension is generated will be acknowledged, if verbose is higher than 3 each processed input file name will be listed. */ { unsigned hi,j; FILE *ifp; FILEHEADER hdr; char ok = 1; int hkpktcnt= -1; LONG time= -1; CCSDSHEADER ccsds; SUTC stim; BYTE hkpkt[LAMPHKPKTLEN-5]; BYTE *bp; int hacktime= 0; int k= 0; const char PSNUM[4]= {0,2,1,3}; // translate from inverted supply numbering resetHackWrap(); if (verbose>2) { printf("### EXTENSION 6: BINARY TABLE: 'Raw' HK Packets list (%ld entrie(s))\n",entries); } for (hi=0;hi3) { printf("#### Hk: %s, start %s\n",file[hfil[hi]].realname ,utcTimeStr(file[hfil[hi]].Start)); } while ((!feof(ifp))&&(ok)) { if (readCcsdsHeader(ifp,&ccsds)) { if (ccsds.apid==LAMPHKAPID) { hkpktcnt= ccsds.pktcnt; if (ccsds.pktlen!=LAMPHKPKTLEN) { printf("WARNING: unexpected HK packet size %d\n",ccsds.pktlen); ok= 0; } readLONG(ifp,&(stim.sec)); readWORD(ifp,&(stim.sub)); readString(ifp,hkpkt,ccsds.pktlen-5); // sec & sub already read hacktime= unwrappedHackTime(hkpkt[24],hkpkt[25]); WriteFitsDOUBLE(ffp,reconstructedSUTC(hacktime,htf)); // 1D: SCUT WriteFitsLONG(ffp,hacktime); // 1J: ext. hack WriteFitsWORD(ffp,ccsds.pktcnt); // 1I: pkt count bp= (BYTE*)&ccsds; // 122B: raw packet for (j=0;j<6;j++) { WriteFitsBYTE(ffp,bp[j]); } WriteFitsLONG(ffp,stim.sec); WriteFitsWORD(ffp,stim.sub); for (j=0;j>4)&7)); // 1B: LAMP state WriteFitsBYTE(ffp,(BYTE)((hkpkt[ 0] )&7)); // 1B: last safety WriteFitsBYTE(ffp,PSNUM[((hkpkt[ 1]>>6)&3)]); // 1B: LVPS WriteFitsBYTE(ffp,PSNUM[((hkpkt[ 1]>>4)&3)]); // 1B: HVPS WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[71])); // 1E: mir tmp set WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[72])); // 1E: grt tmp set WriteFitsBYTE(ffp,(BYTE)((hkpkt[ 2]>>2)&3)); // 1B: mir htr WriteFitsBYTE(ffp,(BYTE)((hkpkt[ 2] )&3)); // 1B: grt htr WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[73])); // 1E: mir tmp A WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[74])); // 1E: mir tmp B WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[75])); // 1E: grt tmp A WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[76])); // 1E: grt tmp B WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[77])); // 1E: c&dh tmp WriteFitsFLOAT(ffp,(float)TempsCal(hkpkt[78])); // 1E: deth tmp WriteFitsBYTE(ffp,(BYTE)((hkpkt[16]>>4)&3)); // 1B: ap door WriteFitsBYTE(ffp,(BYTE)((hkpkt[16]>>2)&1)); // 1B: lts dark WriteFitsBYTE(ffp,(BYTE)((hkpkt[17] )&1)); // 1B: STIM stat WriteFitsFLOAT(ffp,(float)HvSetCal(hkpkt[20])); // 1E: hvsetp WriteFitsFLOAT(ffp,(float)McpVCal(hkpkt[36])); // 1E: mcpv1 WriteFitsFLOAT(ffp,(float)AnoVCal(hkpkt[37])); // 1E: anodev1 WriteFitsFLOAT(ffp,(float)StpICal(hkpkt[38])); // 1E: stripi1 WriteFitsFLOAT(ffp,(float)McpVCal(hkpkt[39])); // 1E: mcpv1 WriteFitsFLOAT(ffp,(float)AnoVCal(hkpkt[40])); // 1E: anodev1 WriteFitsFLOAT(ffp,(float)StpICal(hkpkt[41])); // 1E: stripi1 WriteFitsFLOAT(ffp,(float)McpVCal(hkpkt[42])); // 1E: max mcpv WriteFitsFLOAT(ffp,(float)SumICal(hkpkt[43])); // 1E: max stripi WriteFitsFLOAT(ffp,(float)DiscrCal(hkpkt[44])); // 1E: discrim. WriteFitsLONG(ffp,hkpkt[18]*256+hkpkt[19]); // 1J: countrate WriteFitsWORD(ffp,(WORD)(hkpkt[46]*256+hkpkt[47]));// 1I: lts-a raw WriteFitsWORD(ffp,(WORD)(hkpkt[48]*256+hkpkt[49]));// 1I: lts-b raw WriteFitsBYTE(ffp,(BYTE)(hkpkt[70])); // 1B: LTS cycles WriteFitsBYTE(ffp,(BYTE)(hkpkt[79]&0x3f)); // 1B: Safety WriteFitsWORD(ffp,(WORD)(hkpkt[80]*256+hkpkt[81]));// 1I: safe timer } else if (ccsds.apid=LAMPMDAPID) { skipBYTEs(ifp,ccsds.pktlen+1); } else { printf("WARNING: skipping unexpected packet ApId %d, len %d\n",ccsds.apid,ccsds.pktlen); skipBYTEs(ifp,ccsds.pktlen+1); } } else if (!feof(ifp)) { printf("ERROR: incorrect CCSDS header near %d in file %s\n",ftell(ifp),file[hfil[hi]].realname); ok= 0; } } } else { printf("ERROR: file too small (<64): %s\n",file[hfil[hi]].realname); exit(3); } fclose(ifp); } else { printf("ERROR: cannot access input file: %s\n",file[hfil[hi]].realname); exit(3); } } Fill2880(verbose,ffp,'\0'); } /* WriteFitsHkPacketsExt6() */ /*-----------------------------------------------------------------------*/ int generateFitsFile(char verbose, char correct, unsigned char hfil[], unsigned char sfil[] , FILEHEADER file[], char ddir[], char targetfile[] , HKOVERVIEW *hovr, ACQLIST *acq, SCIOVERVIEW *sovr , IMAGE doh, IMAGE dch) /* ARGS verbose status, correct hack error option, housekeeping files index, science files index, list of all file headers, output file directory path, target file specification, housekeeping overview structure, acquisition list, science overview structure, door open and door closed histogram images RETURNS 1 - completed successfully 0 - failed to generate output file DESCRIPTION This function controls the generation of the complete output FITS file, it generates all the required headers and call the appropriate function to generate each of the FITS data extensions. The function is quite long as it has to contain the format and contents specification of all the generated keyword entries, but the structure is very simple, after opening the output file it just writes a single FITS line for each keyword entry and calls the appropriate function to fill in the data extensions. The function will always print the full name of the generated output file on standard output, for all output the verbose parameter is passed through to the functions that generate the actual FITS file output, so that for a verbose setting of above 2 all generated FITS keywords will also be send to standard output. */ { char tstr1[80]; char tstr2[80]; double tim; unsigned i; FILE *fp; char filename[MAXFILENAME]; long intervals; makeOutputFileName(file[1].Start.sec,ddir,FITEXTEND,filename); printf("# generate FITS output file: %s\n",filename); if ((fp=fopen(filename,FMODEWRITE))!=NULL) { WriteFitsLine(verbose,fp,"SIMPLE" ,"T","LRO-LAMP FITS file conforms to the FITS standard"); WriteFitsLine(verbose,fp,"BITPIX" ,"32","number of bits per data pixel"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"1024","number of spectral channels"); WriteFitsLine(verbose,fp,"NAXIS2" ,"32","number of spatial channels"); WriteFitsLine(verbose,fp,"EXTEND" ,"T","FITS data may contain extensions"); WriteFitsLine(verbose,fp,"BZERO" ,"0",""); WriteFitsLine(verbose,fp,"BSCALE" ,"1",""); WriteFitsLine(verbose,fp,"COMMENT" ,"","mission Data ---------------------------------------------------"); WriteFitsLine(verbose,fp,"MISSION" ,"'LRO'","Lunar Reconnaissance Orbiter"); WriteFitsLine(verbose,fp,"HOSTNAME","'LRO'","host name (PDS terminology)"); WriteFitsLine(verbose,fp,"INSTRUME","'LAMP'","Lyman Alpha Mapping Project"); WriteFitsLine(verbose,fp,"FILETIME",currentUtcTimeStr(tstr1),"time file was created by SOC (UTC)"); WriteFitsLine(verbose,fp,"SL1PROC" ,"'LAMP-LIMA'","SOC pipeline level 1 software"); WriteFitsLine(verbose,fp,"SL1VER" ,QStr(strCopyN(tstr1,&VERSIONSTR[19],4)) ,"SOC pipeline level 1 software version"); WriteFitsLine(verbose,fp,"SL1DATE" ,QStr(strCopyN(tstr1,&VERSIONSTR[30],10)) ,"SOC pipeline level 1 software date"); if (strlen(sfil)>0) { tim= (realSUTC(file[sfil[0]].Start)+realSUTC(file[sfil[strlen(sfil)-1]].Stop))/2; } else { tim= (realSUTC(file[hfil[0]].Start)+realSUTC(file[hfil[strlen(hfil)-1]].Stop))/2; } (void)findTarget(tim,targetfile,tstr1,tstr2); printf("# %14.3f target: %s, activity: %s\n",tim,tstr1,tstr2); WriteFitsLine(verbose,fp,"TARGET",QStr(tstr1),"Observation target"); WriteFitsLine(verbose,fp,"ACTIVITY",QStr(tstr2),"Observation activity"); WriteFitsLine(verbose,fp,"ORIGIN" ,"'SwRI'","Southwest Research Institute"); WriteFitsLine(verbose,fp,"COMMENT" ,"","input source data ----------------------------------------------"); for (i=0;ipixacq>0)&&(sovr->hisacq==0)],"Science file with pixellist data only"); WriteFitsLine(verbose,fp,"HISONLY" ,LogicStr[(sovr->hisacq>0)&&(sovr->pixacq==0)],"Science file with histogram data only"); WriteFitsLine(verbose,fp,"COMMENT" ,"","General HK overview ---------------------------------------------"); WriteFitsLine(verbose,fp,"PACKETS" ,Int2Str(hovr->hkpackets),"Total number of housekeeping packets"); WriteFitsLine(verbose,fp,"HKGAPS" ,LogicStr[hovr->contin==0],"Gaps in HK packet stream"); WriteFitsLine(verbose,fp,"HWVERS" ,QStr(HwVerStr[hovr->hwvers&0x0f]),"Instrument H/W version/platform"); WriteFitsLine(verbose,fp,"SWVERS" ,Flt2Str(((hovr->swvers&0xf0)>>4)+((hovr->swvers&0x0f)/100.0),4,2),"Instrument flight software version"); WriteFitsLine(verbose,fp,"SWCHECK" ,QStr(Hex2Str(hovr->swchck)),"Instrument flight software checksum"); WriteFitsLine(verbose,fp,"LVPSSTAT",QStr(PowerStatStr[hovr->lvpsstate]),"LVPS status"); WriteFitsLine(verbose,fp,"TEMPSFTY",QStr(SafeStatStr[hovr->tsafety]),"Temperature Safety"); WriteFitsLine(verbose,fp,"ANODSFTY",QStr(SafeStatStr[hovr->asafety]),"Anode Voltage Safety"); WriteFitsLine(verbose,fp,"STRPSFTY",QStr(SafeStatStr[hovr->ssafety]),"Strip Current Safety"); WriteFitsLine(verbose,fp,"HVSFTY" ,QStr(SafeStatStr[hovr->hsafety]),"High Voltage Safety"); WriteFitsLine(verbose,fp,"BRTSFTY" ,QStr(SafeStatStr[hovr->bsafety]),"Bright Object Safety"); WriteFitsLine(verbose,fp,"CYCLSFTY",QStr(SafeStatStr[hovr->csafety]),"Cycle Safety"); WriteFitsLine(verbose,fp,"LASTSFTY",QStr(LastSafeStr[hovr->lastsafe]),"Last Active Safety"); WriteFitsLine(verbose,fp,"HACKCLCK",Flt2Str(hovr->htf[0].hackrate,14,12),"Instrument actual hack clock (SCUT<->hackclk)"); WriteFitsLine(verbose,fp,"HACKOFFS",Flt2Str(hovr->htf[0].hackoffset,14,3),"Instrument hack clock offset (SCUT<->hackclk)"); WriteFitsLine(verbose,fp,"HACKCORR",Flt2Str(hovr->htf[0].hackcorr,9,6),"Instrument hack clock correlation"); if (hovr->timeframes>1) // multiple hack time frames { WriteFitsLine(verbose,fp,"HACKLAST",Flt2Str(hovr->htf[0].lasthack,10,0),"Last hack time with first fit parameters"); WriteFitsLine(verbose,fp,"HACKFRMS",Int2Str(hovr->timeframes),"Multiple hack time frames (restarts)"); for (i=1;i<(unsigned)hovr->timeframes;i++) { WriteFitsLine(verbose,fp,NumiStr("HACKCLCK",i),Flt2Str(hovr->htf[i].hackrate,14,12),"Instrument actual hack clock (SCUT<->hackclk)"); WriteFitsLine(verbose,fp,NumiStr("HACKOFFS",i),Flt2Str(hovr->htf[i].hackoffset,14,3),"Instrument hack clock offset (SCUT<->hackclk)"); WriteFitsLine(verbose,fp,NumiStr("HACKCORR",i),Flt2Str(hovr->htf[i].hackcorr,9,6),"Instrument hack clock correlation"); WriteFitsLine(verbose,fp,NumiStr("HACKLAST",i),Flt2Str(hovr->htf[0].lasthack,10,0),"Last hack time with current fit parameters"); } } WriteFitsLine(verbose,fp,"COMMENT" ,"","HVPS HK overview ------------------------------------------------"); WriteFitsLine(verbose,fp,"HVPSSTAT",QStr(PowerStatStr[hovr->hvpsstate]),"HVPS status"); WriteFitsLine(verbose,fp,"HVLTSCTL",QStr(LtsStatStr[hovr->ltsstate]),"HVPS LTS control"); WriteFitsLine(verbose,fp,"HVSETMIN",Flt2Str(HvSetCal(hovr->hvsetmax),6,3),"Maximum HV set Voltage (kVolt)"); WriteFitsLine(verbose,fp,"HVSETMAX",Flt2Str(HvSetCal(hovr->hvsetmin),6,3),"Minimum HV set Voltage (kVolt)"); WriteFitsLine(verbose,fp,"MCPV1MIN",Flt2Str(McpVCal(hovr->mcpv1max),6,3),"Maximum Mcp Voltage 1 (kVolt)"); WriteFitsLine(verbose,fp,"MCPV1MAX",Flt2Str(McpVCal(hovr->mcpv1min),6,3),"Minimum Mcp Voltage 1 (kVolt)"); WriteFitsLine(verbose,fp,"STPI1MIN",Flt2Str(StpICal(hovr->stpi1min),5,2),"Minimum Strip Current 1 (uA)"); WriteFitsLine(verbose,fp,"STPI1MAX",Flt2Str(StpICal(hovr->stpi1max),5,2),"Maximum Strip Current 1 (uA)"); WriteFitsLine(verbose,fp,"ANOV1MIN",Flt2Str(AnoVCal(hovr->anov1max),4,0),"Maximum Anode Voltage 1 (Volt)"); WriteFitsLine(verbose,fp,"ANOV1MAX",Flt2Str(AnoVCal(hovr->anov1min),4,0),"Minimum Anode Voltage 1 (Volt)"); WriteFitsLine(verbose,fp,"MCPV2MIN",Flt2Str(McpVCal(hovr->mcpv2max),6,3),"Maximum Mcp Voltage 2 (kVolt)"); WriteFitsLine(verbose,fp,"MCPV2MAX",Flt2Str(McpVCal(hovr->mcpv2min),6,3),"Minimum Mcp Voltage 2 (kVolt)"); WriteFitsLine(verbose,fp,"STPI2MIN",Flt2Str(StpICal(hovr->stpi2min),5,2),"Minimum Strip Current 2 (uA)"); WriteFitsLine(verbose,fp,"STPI2MAX",Flt2Str(StpICal(hovr->stpi2max),5,2),"Maximum Strip Current 2 (uA)"); WriteFitsLine(verbose,fp,"ANOV2MIN",Flt2Str(AnoVCal(hovr->anov2max),4,0),"Maximum Anode Voltage 2 (Volt)"); WriteFitsLine(verbose,fp,"ANOV2MAX",Flt2Str(AnoVCal(hovr->anov2min),4,0),"Minimum Anode Voltage 2 (Volt)"); if (hovr->hvopslvl>0) { WriteFitsLine(verbose,fp,"HVOPSLVL",Flt2Str(HvSetCal(hovr->hvopslvl),6,3),"HV(set) operational level (kVolt)"); } else { WriteFitsLine(verbose,fp,"HVOPSLVL","'UNKNOWN'","HV(set) operational level (kVolt)"); } WriteFitsLine(verbose,fp,"COMMENT" ,"","Detector HK overview --------------------------------------------"); WriteFitsLine(verbose,fp,"MINDISC" ,Flt2Str(DiscrCal(hovr->discvmin),4,2),"Minimum discriminator Voltage (Volt)"); WriteFitsLine(verbose,fp,"MAXDISC" ,Flt2Str(DiscrCal(hovr->discvmax),4,2),"Maximum discriminator Voltage (Volt)"); WriteFitsLine(verbose,fp,"EVCNTLO" ,Int2Str(hovr->evcntlo),"Lowest value of 24 bit HW raw event counter"); WriteFitsLine(verbose,fp,"EVCNTHI" ,Int2Str(hovr->evcnthi),"Highest value of 24 bit HW raw event counter"); WriteFitsLine(verbose,fp,"EVCOUNTS",Int2Str(hovr->evcounts),"Total number of raw events reported"); WriteFitsLine(verbose,fp,"MINCNTR" ,Int2Str(hovr->mincntr),"Minimum Countrate (Hz)"); WriteFitsLine(verbose,fp,"AVRCNTR" ,Flt2Str(average(1.0*hovr->evcounts,hovr->hkpackets),8,1),"Average Countrate (Hz)"); WriteFitsLine(verbose,fp,"MAXCNTR" ,Int2Str(hovr->maxcntr),"Maximum Countrate (Hz)"); WriteFitsLine(verbose,fp,"APDOOR" ,QStr(ApDoorStatStr[hovr->apdstate]),"Aperture door status during whole period"); WriteFitsLine(verbose,fp,"COMMENT" ,"","Temperature HK overview -----------------------------------------"); WriteFitsLine(verbose,fp,"MINCDHT" ,Flt2Str(TempsCal(hovr->cndhtmin),5,1),"C&DH minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRCDHT" ,Flt2Str(hovr->cndhtsum/hovr->hkpackets,5,1),"C&DH average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXCDHT" ,Flt2Str(TempsCal(hovr->cndhtmax),5,1),"C&DH maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MINDETHT",Flt2Str(TempsCal(hovr->dethtmin),5,1),"Detector Housing minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRDETHT",Flt2Str(hovr->dethtsum/hovr->hkpackets,5,1),"Detector Housing average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXDETHT",Flt2Str(TempsCal(hovr->dethtmax),5,1),"Detector Housing maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MINMIRAT",Flt2Str(TempsCal(hovr->miratmin),5,1),"Mirror A minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRMIRAT",Flt2Str(hovr->miratsum/hovr->hkpackets,5,1),"Mirror A average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXMIRAT",Flt2Str(TempsCal(hovr->miratmax),5,1),"Mirror A maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MINMIRBT",Flt2Str(TempsCal(hovr->mirbtmin),5,1),"Mirror B minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRMIRBT",Flt2Str(hovr->mirbtsum/hovr->hkpackets,5,1),"Mirror B average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXMIRBT",Flt2Str(TempsCal(hovr->mirbtmax),5,1),"Mirror B maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MINGRTAT",Flt2Str(TempsCal(hovr->grtatmin),5,1),"Grating A minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRGRTAT",Flt2Str(hovr->grtatsum/hovr->hkpackets,5,1),"Grating A average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXGRTAT",Flt2Str(TempsCal(hovr->grtatmax),5,1),"Grating A maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MINGRTBT",Flt2Str(TempsCal(hovr->grtbtmin),5,1),"Grating B minimum temperature (degC)"); WriteFitsLine(verbose,fp,"AVRGRTBT",Flt2Str(hovr->grtbtsum/hovr->hkpackets,5,1),"Grating B average temperature (degC)"); WriteFitsLine(verbose,fp,"MAXGRTBT",Flt2Str(TempsCal(hovr->grtbtmax),5,1),"Grating B maximum temperature (degC)"); WriteFitsLine(verbose,fp,"MIRHTRST",QStr(HtrStatStr[hovr->mirhtrstate]),"Mirror heaters status"); WriteFitsLine(verbose,fp,"GRTHTRST",QStr(HtrStatStr[hovr->grthtrstate]),"Grating heaters status"); WriteFitsLine(verbose,fp,"DETDOOR" ,QStr(DetDoorStatStr[hovr->detdstate]),"Detector door status"); WriteFitsLine(verbose,fp,"COMMENT" ,"","Spectral Image; Door Open Status (ground calculated)"); WriteFitsLine(verbose,fp,"COMMENT" ,"","contains first histogram if any histograms were present"); WriteFitsLine(verbose,fp,"DOEVTN" ,Int2Str(sovr->doevts),"Door Open histogram total digital events"); WriteFitsLine(verbose,fp,"DOTIME" ,Flt2Str(sovr->dotime,8,3),"Door Open histogram acquistion time (sec)"); WriteFitsLine(verbose,fp,"DOSOURCE",QStr(HisGndStr[sovr->hisacq>=1]),"Door Open Histogram data source (gnd/his)"); WriteFitsLine(verbose,fp,"DOMAXCNT",Int2Str(sovr->domaxcnt),"Door Open histogram maximum counts"); WriteFitsLine(verbose,fp,"DOMINCNT",Int2Str(sovr->domincnt),"Door Open histogram minimum counts"); WriteFitsLine(verbose,fp,"DOMAXCR" ,Int2Str(sovr->domaxcr),"Door Open histogram maximum countrate (Hz)"); WriteFitsLine(verbose,fp,"DOMINCR" ,Int2Str(sovr->domincr),"Door Open histogram minimum countrate (Hz)"); WriteFitsLine(verbose,fp,"DOPATTRN",QStr(TestPatternStr[scienceType(sovr->dochecksum)]) ,"Door Open histogram data pattern"); WriteFitsLine(verbose,fp,"DOLTS" ,QStr(LtsStatStr[sovr->dolts]),"Door Open histogram LTS status"); WriteFitsLine(verbose,fp,"DOSTIMS" ,QStr(StimStatStr[sovr->dostims]),"Door Open histogram STIM status"); WriteFitsLine(verbose,fp,"DOHVPS" ,QStr(PowerStatStr[sovr->dohvps]),"Door Open histogram HVPS status"); WriteFitsLine(verbose,fp,"END" ,"",""); Fill2880(verbose,fp,' '); WriteFitsImage(verbose,fp,doh,"PRIMARY DATA: SPECTRAL IMAGE: DOOR OPEN HISTOGRAM 32 x 1024 x 32-bits"); /*------------------------------------------------------------------------*/ WriteFitsLine(verbose,fp,"XTENSION" ,"'IMAGE '","Extension 1: Spectral Image Door Closed"); WriteFitsLine(verbose,fp,"BITPIX" ,"32","Number of bits per data pixel"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","Number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"1024","Number of spectral channels"); WriteFitsLine(verbose,fp,"NAXIS2" ,"32","Number of spatial channels"); WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"BZERO" ,"0",""); WriteFitsLine(verbose,fp,"BSCALE" ,"1",""); WriteFitsLine(verbose,fp,"EXTNAME" ,"'Spectral Image (Door Closed)'",""); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"COMMENT" ,"","Spectral Image; Door Closed Status (ground calculated)"); WriteFitsLine(verbose,fp,"COMMENT" ,"","contains second histogram if two or more histograms were present"); WriteFitsLine(verbose,fp,"DCEVTN" ,Int2Str(sovr->dcevts),"Door Close histogram total digital events"); WriteFitsLine(verbose,fp,"DCTIME" ,Flt2Str(sovr->dctime,8,3),"Door Close histogram acquistion time (sec)"); WriteFitsLine(verbose,fp,"DCSOURCE",QStr(HisGndStr[sovr->hisacq>=2]),"Door Close Histogram data source (gnd/his)"); WriteFitsLine(verbose,fp,"DCMAXCNT",Int2Str(sovr->dcmaxcnt),"Door Close histogram maximum counts"); WriteFitsLine(verbose,fp,"DCMINCNT",Int2Str(sovr->dcmincnt),"Door Close histogram minimum counts"); WriteFitsLine(verbose,fp,"DCMAXCR" ,Int2Str(sovr->dcmaxcr),"Door Close histogram maximum countrate (Hz)"); WriteFitsLine(verbose,fp,"DCMINCR" ,Int2Str(sovr->dcmincr),"Door Close histogram minimum countrate (Hz)"); WriteFitsLine(verbose,fp,"DCPATTRN",QStr(TestPatternStr[scienceType(sovr->dcchecksum)]) ,"Door Close histogram data pattern"); WriteFitsLine(verbose,fp,"DCLTS" ,QStr(LtsStatStr[sovr->dclts]),"Door Close histogram LTS status"); WriteFitsLine(verbose,fp,"DCSTIMS" ,QStr(StimStatStr[sovr->dcstims]),"Door Close histogram STIM status"); WriteFitsLine(verbose,fp,"DCHVPS" ,QStr(PowerStatStr[sovr->dchvps]),"Door Close histogram HVPS status"); WriteFitsLine(verbose,fp,"END" ,"",""); Fill2880(verbose,fp,' '); WriteFitsImage(verbose,fp,dch,"EXTENSION 1: SPECTRAL IMAGE: DOOR CLOSE HISTOGRAM 32 x 1024 x 32-bits"); /*------------------------------------------------------------------------*/ WriteFitsLine(verbose,fp,"XTENSION","'TABLE '","Extension 2: Acquisition List (HK+SCI)"); WriteFitsLine(verbose,fp,"BITPIX" ,"8","8-bit ascii characters"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"64","number of characters per line"); WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(acq->acquisitions),"number of acquisitions"); WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"TFIELDS" ,"12","number of element fiels per line"); WriteFitsLine(verbose,fp,"EXTNAME" ,"'Acquisition List'","Acquisition/frames list from HK and SCI"); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"TFORM1" ,"'D14.3 '","format field 1: float with 3 decimal places"); WriteFitsLine(verbose,fp,"TFORM2" ,"'D14.3 '","format field 2: float with 3 decimal places"); WriteFitsLine(verbose,fp,"TFORM3" ,"'I5 '","format field 3: five digit integer"); WriteFitsLine(verbose,fp,"TFORM4" ,"'I1 '","format field 4: one digit integer"); WriteFitsLine(verbose,fp,"TFORM5" ,"'I4 '","format field 5: four digit integer"); WriteFitsLine(verbose,fp,"TFORM6" ,"'I1 '","format field 6: one digit integer"); WriteFitsLine(verbose,fp,"TFORM7" ,"'I1 '","format field 7: one digit integer"); WriteFitsLine(verbose,fp,"TFORM8" ,"'I1 '","format field 8: one digit integer"); WriteFitsLine(verbose,fp,"TFORM9" ,"'I1 '","format field 9: one digit integer"); WriteFitsLine(verbose,fp,"TFORM10" ,"'I1 '","format field 10: one digit integer"); WriteFitsLine(verbose,fp,"TFORM11" ,"'I2 '","format field 11: one digit integer"); WriteFitsLine(verbose,fp,"TFORM12" ,"'I8 '","format field 12: eight digit integer"); WriteFitsLine(verbose,fp,"TBCOL1" ,"1" ,"start column for field 1"); WriteFitsLine(verbose,fp,"TBCOL2" ,"16","start column for field 2"); WriteFitsLine(verbose,fp,"TBCOL3" ,"31","start column for field 3"); WriteFitsLine(verbose,fp,"TBCOL4" ,"37","start column for field 4"); WriteFitsLine(verbose,fp,"TBCOL5" ,"39","start column for field 5"); WriteFitsLine(verbose,fp,"TBCOL6" ,"44","start column for field 6"); WriteFitsLine(verbose,fp,"TBCOL7" ,"46","start column for field 7"); WriteFitsLine(verbose,fp,"TBCOL8" ,"48","start column for field 8"); WriteFitsLine(verbose,fp,"TBCOL9" ,"50","start column for field 9"); WriteFitsLine(verbose,fp,"TBCOL10" ,"52","start column for field 10"); WriteFitsLine(verbose,fp,"TBCOL11" ,"54","start column for field 11"); WriteFitsLine(verbose,fp,"TBCOL12" ,"57","start column for field 12"); WriteFitsLine(verbose,fp,"TTYPE1" ,"'START_TIME'","start time of acquisition (sec since 1/1/2001)"); WriteFitsLine(verbose,fp,"TTYPE2" ,"'STOP_TIME'","stop time of acquisition (sec since 1/1/2001)"); WriteFitsLine(verbose,fp,"TTYPE3" ,"'SCI_HDR'","science frame header from HK stream"); WriteFitsLine(verbose,fp,"TTYPE4" ,"'ACQ_MODE'","acquistion type (0-pixel,1-histo)"); WriteFitsLine(verbose,fp,"TTYPE5" ,"'FRAME_NO'","frame number (0-4095)"); WriteFitsLine(verbose,fp,"TTYPE6" ,"'APD_STATE'","apdoor state (1-err,2-cls,3-opn,4-btwn,5-var)"); WriteFitsLine(verbose,fp,"TTYPE7" ,"'LTS_STATE'","LTS state (1-day,2-dark,5-var)"); WriteFitsLine(verbose,fp,"TTYPE8" ,"'STIM_STATE'","STIM state (1-off,2-on,5-var)"); WriteFitsLine(verbose,fp,"TTYPE9" ,"'HVPS_STATE'","HVPS state (1-off,2-A,3-B,4-both,5-var)"); WriteFitsLine(verbose,fp,"TTYPE10" ,"'HACK_RATE'","Hack Rate (0-N/A, 1-4ms, 2-8ms,... ,8-512ms)"); WriteFitsLine(verbose,fp,"TTYPE11" ,"'FR_PATTERN'","Data pattern (0-data, 1-no frame, 2- test patterns)"); WriteFitsLine(verbose,fp,"TTYPE12" ,"'CHECKSUM'","Frame data checksum (3 bytes)"); WriteFitsLine(verbose,fp,"TUNIT1" ,"'sec '","unit for SUTC"); WriteFitsLine(verbose,fp,"TUNIT2" ,"'sec '","unit for SUTC"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"NHKACQS" ,Int2Str(acq->acquisitions),"number of acquisition reported in HK data"); WriteFitsLine(verbose,fp,"CONACQS" ,LogicStr[acq->contin],"all reported acquisitions contineous"); if (acq->acquisitions>0) { WriteFitsLine(verbose,fp,"FACQSTA" ,Flt2Str(acq->ac[0].start,14,3),"first acquisition start time"); WriteFitsLine(verbose,fp,"LACQEND" ,Flt2Str(acq->ac[acq->acquisitions-1].stop,14,3),"last acquisition end time"); } else { WriteFitsLine(verbose,fp,"FACQSTA" ,Flt2Str(-1.0,14,3),"first acquisition start time"); WriteFitsLine(verbose,fp,"LACQEND" ,Flt2Str(-1.0,14,3),"last acquisition end time"); } WriteFitsLine(verbose,fp,"PIXONLY" ,LogicStr[(acq->pixacq>0)&&(acq->hisacq==0)],"pixellist acquisitions only"); WriteFitsLine(verbose,fp,"HISONLY" ,LogicStr[(acq->hisacq>0)&&(acq->pixacq==0)],"histogram acquisitions only"); WriteFitsLine(verbose,fp,"END","",""); Fill2880(verbose,fp,' '); WriteFitsAcquisitionListExt2(verbose,fp,acq,hovr->htf); /*------------------------------------------------------------------------*/ WriteFitsLine(verbose,fp,"XTENSION","'BINTABLE'","Extension 3: Raw Science Frame Data (SCI)"); WriteFitsLine(verbose,fp,"BITPIX" ,"8","array data type, 8-bit bytes"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"65544","number of bytes per frame"); // 65536+8 WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(sovr->hisacq+sovr->pixacq),"number of science frames"); WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"TFIELDS" ,"2","number of element fields per line"); WriteFitsLine(verbose,fp,"EXTNAME" ,"'Raw Frame Data'","Histrogram and Pixellist frame data (SCI)"); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"TTYPE1" ,"'GEN_TIME'","frame generation time"); WriteFitsLine(verbose,fp,"TTYPE2" ,"'FRAME_DATA'","raw frame data"); WriteFitsLine(verbose,fp,"TFORM1" ,"'D '","8 byte double"); // 8 byte double WriteFitsLine(verbose,fp,"TFORM2" ,"'32768I '","32768 2 byte integers (unsigned offset)"); // 4*32768 = 131072 WriteFitsLine(verbose,fp,"TZERO2" ,"32768","zero offset to get unsigned integers"); WriteFitsLine(verbose,fp,"TDISP1" ,"'D14.3 '","preferred display format for time"); WriteFitsLine(verbose,fp,"TUNIT1" ,"'sec '","unit for SUTC"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"HACKFIX" ,Int2Str(sovr->hackfixes),"number of corrected hack bit flips"); WriteFitsLine(verbose,fp,"NFRAMES" ,Int2Str(sovr->hisacq+sovr->pixacq),"number of science frames"); if (acq->acquisitions>0) { WriteFitsLine(verbose,fp,"FRMONE" ,Int2Str(acq->ac[0].frameno),"first frame number"); WriteFitsLine(verbose,fp,"FRMLAST" ,Int2Str(acq->ac[acq->acquisitions-1].frameno),"last frame number"); } else { WriteFitsLine(verbose,fp,"FRMONE" ,Int2Str(-1),"first frame number"); WriteFitsLine(verbose,fp,"FRMLAST" ,Int2Str(-1),"last frame number"); } WriteFitsLine(verbose,fp,"CONTFRM" ,LogicStr[acq->contin],"continuous time covered by frames"); WriteFitsLine(verbose,fp,"FRMDATA" ,QStr(FrmDataStr[sovr->hisacq>0][sovr->pixacq>0]),"frame data type"); WriteFitsLine(verbose,fp,"END" ,"",""); Fill2880(verbose,fp,' '); WriteFitsFramesExt3(verbose,correct,fp,sfil,file,sovr->hisacq+sovr->pixacq,sovr->hackfixes); /*------------------------------------------------------------------------*/ // This dataset will be left empty when no pixellist data is present, in // this case filling the extension with HK based countrate data does not // make much sense since this is different data is already available in // a direct accessible format in the HK extension. WriteFitsLine(verbose,fp,"XTENSION","'BINTABLE'","Extension 4: Calculated Countrate (SCI)"); WriteFitsLine(verbose,fp,"BITPIX" ,"8","array data type, 8-bit bytes"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"16","number of bytes per entry"); intervals= determineHackIntervals(verbose,fp,sfil,file,acq,hovr->htf); if (sovr->hacks>1) { WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(intervals),"Number of countrate entries (Pixellist)"); } else { WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(0),"No pixellist data available"); } WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"TFIELDS" ,"3","number of element fields per line"); WriteFitsLine(verbose,fp,"EXTNAME" ,"'Calculated Countrate'","From Pixellist Science data"); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"TTYPE1" ,"'HACK_TIME'","hack time at start of interval (wrap extended)"); WriteFitsLine(verbose,fp,"TTYPE2" ,"'SCUT_TIME'","hacktime converted into spacecraft UTC (sec)"); WriteFitsLine(verbose,fp,"TTYPE3" ,"'COUNT_RATE'","countrate for the interval (Hz)"); WriteFitsLine(verbose,fp,"TFORM1" ,"'J '","4 byte long"); WriteFitsLine(verbose,fp,"TFORM2" ,"'D '","8 byte double (calculated from hacktime)"); WriteFitsLine(verbose,fp,"TFORM3" ,"'J '","4 byte long"); WriteFitsLine(verbose,fp,"TDISP2" ,"'D14.3 '","preferred display format for time"); WriteFitsLine(verbose,fp,"TUNIT2" ,"'sec '","unit for SUTC"); WriteFitsLine(verbose,fp,"TUNIT3" ,"'Hz '","unit for Countrate"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"CRTIME" ,Flt2Str(sovr->crtime,8,3),"time span for countrate data"); WriteFitsLine(verbose,fp,"MINCR" ,Int2Str(sovr->mincr),"overall minimum countrate (Hz)"); WriteFitsLine(verbose,fp,"AVRCR" ,Flt2Str(average(sovr->pixevents,sovr->crtime),6,1),"overall average countrate (Hz)"); WriteFitsLine(verbose,fp,"MAXCR" ,Int2Str(sovr->maxcr),"overall maximum countrate (Hz)"); WriteFitsLine(verbose,fp,"MINCRDO" ,Int2Str(sovr->mincrdo),"door open minimum countrate (Hz)"); WriteFitsLine(verbose,fp,"AVRCRDO" ,Flt2Str(average(sovr->evtcrdo,sovr->timecrdo),6,1),"door open average countrate (Hz)"); WriteFitsLine(verbose,fp,"MAXCRDO" ,Int2Str(sovr->maxcrdo),"door open maximum countrate (Hz)"); WriteFitsLine(verbose,fp,"MINCRDC" ,Int2Str(sovr->mincrdc),"door close minimum countrate (Hz)"); WriteFitsLine(verbose,fp,"AVRCRDC" ,Flt2Str(average(sovr->evtcrdc,sovr->timecrdc),6,1),"door close average countrate (Hz)"); WriteFitsLine(verbose,fp,"MAXCRDC" ,Int2Str(sovr->maxcrdc),"door close maximum countrate (Hz)"); WriteFitsLine(verbose,fp,"END","",""); Fill2880(verbose,fp,' '); WriteFitsCountratesExt4(verbose,fp,sfil,file,acq,hovr->htf,intervals); /*------------------------------------------------------------------------*/ WriteFitsLine(verbose,fp,"XTENSION","'BINTABLE'","Extension 5: LTS Data (HK)"); WriteFitsLine(verbose,fp,"BITPIX" ,"8","array data type, 8-bit bytes"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"14","number of bytes per entry"); WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(hovr->ltsentries),"number of LTS entries"); WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"TFIELDS" ,"4","number of element fields per line"); WriteFitsLine(verbose,fp,"EXTNAME" ,"'LTS Data'","High rate (10 Hz) low resolution (8-bit) from HK"); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"TTYPE1" ,"'HACK_TIME'","hack time at start of interval (wrap extended)"); WriteFitsLine(verbose,fp,"TTYPE2" ,"'SCUT_TIME'","hacktime converted into spacecraft UTC (sec since epoch)"); WriteFitsLine(verbose,fp,"TTYPE3" ,"'LTS_A'","LTS A high rate scaled value"); WriteFitsLine(verbose,fp,"TTYPE4" ,"'LTS_B'","LTS B high rate scaled value"); WriteFitsLine(verbose,fp,"TFORM1" ,"'J '","4 byte long"); WriteFitsLine(verbose,fp,"TFORM2" ,"'D '","8 byte double (calculated from hacktime)"); WriteFitsLine(verbose,fp,"TFORM3" ,"'B '","one byte"); WriteFitsLine(verbose,fp,"TFORM4" ,"'B '","one byte"); WriteFitsLine(verbose,fp,"TDISP2" ,"'D14.3 '","preferred display format for time"); WriteFitsLine(verbose,fp,"TUNIT2" ,"'sec '","unit for SUTC"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); // Note LTS operation is coupled to the opposite LVPS supply, LTS data is considered valid if // either the both or the matching LVPS is powered all the time, if the power supply // configuration changes during the operations this may result in an indication that the // LTS datais not valid even though it was valid a part of the time, but this reporting can // not distinguish such cases. WriteFitsLine(verbose,fp,"LTSAVALD",LogicStr[(hovr->lvpsstate==3)||(hovr->lvpsstate==4)],"Always present, but may be invalid"); WriteFitsLine(verbose,fp,"LTSBVALD",LogicStr[(hovr->lvpsstate==2)||(hovr->lvpsstate==4)],"Always present, but may be invalid"); WriteFitsLine(verbose,fp,"LTSTVALD",LogicStr[hovr->ltstime],"Always present, but may be invalid"); WriteFitsLine(verbose,fp,"LTSAMIN" ,Int2Str(hovr->ltsamin),"LTS A minimum value"); WriteFitsLine(verbose,fp,"LTSAAVR" ,Flt2Str(average(hovr->ltsasum,hovr->ltsentries),6,1),"LTS A average value"); WriteFitsLine(verbose,fp,"LTSAMAX" ,Int2Str(hovr->ltsamax),"LTS A maximum value"); WriteFitsLine(verbose,fp,"LTSBMIN" ,Int2Str(hovr->ltsbmin),"LTS B minimum value"); WriteFitsLine(verbose,fp,"LTSBAVR" ,Flt2Str(average(hovr->ltsbsum,hovr->ltsentries),6,1),"LTS B average value"); WriteFitsLine(verbose,fp,"LTSBMAX" ,Int2Str(hovr->ltsbmax),"LTS B maximum value"); WriteFitsLine(verbose,fp,"END" ,"",""); Fill2880(verbose,fp,' '); WriteFitsHighLtsExt5(verbose,fp,hfil,file,hovr->htf,hovr->ltsentries); /*------------------------------------------------------------------------*/ WriteFitsLine(verbose,fp,"XTENSION","'BINTABLE'","Extension 6: Housekeeping Data (HK)"); WriteFitsLine(verbose,fp,"BITPIX" ,"8","array data type, 8-bit bytes"); WriteFitsLine(verbose,fp,"NAXIS" ,"2","number of array dimensions"); WriteFitsLine(verbose,fp,"NAXIS1" ,"229","number of bytes per entry"); WriteFitsLine(verbose,fp,"NAXIS2" ,Int2Str(hovr->hkpackets),"number of HK packets"); WriteFitsLine(verbose,fp,"PCOUNT" ,"0",""); WriteFitsLine(verbose,fp,"GCOUNT" ,"1",""); WriteFitsLine(verbose,fp,"TFIELDS" ,"37","number of element fields per line"); WriteFitsLine(verbose,fp,"EXTNAME" ,"'Housekeeping Data'","Full contents of all HK packets"); WriteFitsLine(verbose,fp,"EXTVER" ,"1","Extension version number"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"TTYPE1" ,"'SCUT_TIME'","spacecraft UTC (sec since epoch from HK)"); WriteFitsLine(verbose,fp,"TTYPE2" ,"'HACK_TIME'","extended instrument hack time (since hk on)"); WriteFitsLine(verbose,fp,"TTYPE3" ,"'PACK_CNT'","14-bit packet count (since hk on)"); WriteFitsLine(verbose,fp,"TTYPE4" ,"'PACKET_DATA'","raw HK CCSDS packet (122 bytes)"); WriteFitsLine(verbose,fp,"TTYPE5" ,"'INST_STATE'","instrument state (2,6-SAFE,1,5-CHK,3-PIX,7-HIS)"); WriteFitsLine(verbose,fp,"TTYPE6" ,"'LAST_SAFETY'","last safety (0-none)"); WriteFitsLine(verbose,fp,"TTYPE7" ,"'LVPS_STAT'","active LVPS (1-A, 2-B, 3-both)"); WriteFitsLine(verbose,fp,"TTYPE8" ,"'HVPS_STAT'","active HVPS (0-off, 1-A, 2-B, 3-both)"); WriteFitsLine(verbose,fp,"TTYPE9" ,"'MIRR_TMP_SET'","mirror temperature set point (degC)"); WriteFitsLine(verbose,fp,"TTYPE10" ,"'GRAT_TMP_SET'","grating temperature set point (degC)"); WriteFitsLine(verbose,fp,"TTYPE11" ,"'MIRR_HTR_STAT'","mirror heater status (0-off, 1-A, 2-B, 3-both)"); WriteFitsLine(verbose,fp,"TTYPE12" ,"'GRAT_HTR_STAT'","grating heater status (0-off, 1-A, 2-B, 3-both)"); WriteFitsLine(verbose,fp,"TTYPE13" ,"'MIRR_A_TMP'","mirror temperature A (primary) (degC)"); WriteFitsLine(verbose,fp,"TTYPE14" ,"'MIRR_B_TMP'","mirror temperature B (redundant) (degC)"); WriteFitsLine(verbose,fp,"TTYPE15" ,"'GRAT_A_TMP'","grating temperature A (primary) (degC)"); WriteFitsLine(verbose,fp,"TTYPE16" ,"'GRAT_B_TMP'","grating temperature B (redundant) (degC)"); WriteFitsLine(verbose,fp,"TTYPE17" ,"'CDH_ELEC_TMP'","c&dh electronics temperature (degC)"); WriteFitsLine(verbose,fp,"TTYPE18" ,"'DET_HOUS_TMP'","detector housing temperature (degC)"); WriteFitsLine(verbose,fp,"TTYPE19" ,"'APDOOR'","Aperture Door (1-closed, 2-open, 3-between)"); WriteFitsLine(verbose,fp,"TTYPE20" ,"'LTS_DARK'","LTS Dark Status (0-light, 1-dark)"); WriteFitsLine(verbose,fp,"TTYPE21" ,"'STIM_STAT'","STIM Status (0-off, 1-on)"); WriteFitsLine(verbose,fp,"TTYPE22" ,"'HV_SETPNT'","Detector HVPS setpoint (kVolt)"); WriteFitsLine(verbose,fp,"TTYPE23" ,"'MCPV1'","Detector Mcp Voltage 1 (kVolt)"); WriteFitsLine(verbose,fp,"TTYPE24" ,"'ANODEV1'","Detector Anode Voltage 1 (Volt)"); WriteFitsLine(verbose,fp,"TTYPE25" ,"'STRIPI1'","Detector Strip Current 1 (uA)"); WriteFitsLine(verbose,fp,"TTYPE26" ,"'MCPV2'","Detector Mcp Voltage 2 (kVolt)"); WriteFitsLine(verbose,fp,"TTYPE27" ,"'ANODEV2'","Detector Anode Voltage 2 (Volt)"); WriteFitsLine(verbose,fp,"TTYPE28" ,"'STRIPI2'","Detector Strip Current 2 (uA)"); WriteFitsLine(verbose,fp,"TTYPE29" ,"'MAX_MCPV'","Maximum Mcp Voltage last second (kVolt)"); WriteFitsLine(verbose,fp,"TTYPE30" ,"'MAX_STRIPI'","Maximum Summed Strip Current last second (uA)"); WriteFitsLine(verbose,fp,"TTYPE31" ,"'DISCRIM'","Detector Discriminator Voltage (Volt)"); WriteFitsLine(verbose,fp,"TTYPE32" ,"'COUNT_RATE'","Analog (raw) countrate (Hz)"); WriteFitsLine(verbose,fp,"TTYPE33" ,"'RAW_LTS_A'","Raw LTS A (14 bits, raw counts)"); WriteFitsLine(verbose,fp,"TTYPE34" ,"'RAW_LTS_B'","Raw LTS B (14 bits, raw counts)"); WriteFitsLine(verbose,fp,"TTYPE35" ,"'LTS_CYCLES'","LTS safing cycles in acquisition"); WriteFitsLine(verbose,fp,"TTYPE36" ,"'SAFETY'","Current Safety Status (0-none pending)"); WriteFitsLine(verbose,fp,"TTYPE37" ,"'SAFE_TIMER'","Safety timeout timer (sec remaining)"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"TFORM1" ,"'D '","8 byte double"); WriteFitsLine(verbose,fp,"TFORM2" ,"'J '","4 byte long"); WriteFitsLine(verbose,fp,"TFORM3" ,"'I '","2 byte integer"); WriteFitsLine(verbose,fp,"TFORM4" ,"'122B '","122 byte"); WriteFitsLine(verbose,fp,"TFORM5" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM6" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM7" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM8" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM9" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM10" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM11" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM12" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM13" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM14" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM15" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM16" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM17" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM18" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM19" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM20" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM21" ,"'B '","1 byte state"); WriteFitsLine(verbose,fp,"TFORM22" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM23" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM24" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM25" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM26" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM27" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM28" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM29" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM30" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM31" ,"'E '","4 byte float"); WriteFitsLine(verbose,fp,"TFORM32" ,"'J '","4 byte long"); WriteFitsLine(verbose,fp,"TFORM33" ,"'I '","2 byte integer"); WriteFitsLine(verbose,fp,"TFORM34" ,"'I '","2 byte integer"); WriteFitsLine(verbose,fp,"TFORM35" ,"'B '","1 byte integer"); WriteFitsLine(verbose,fp,"TFORM36" ,"'B '","1 byte status"); WriteFitsLine(verbose,fp,"TFORM37" ,"'I '","2 byte integer"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"TDISP1" ,"'D14.3 '","preferred display format for time"); WriteFitsLine(verbose,fp,"TUNIT1" ,"'sec '","unit for time"); WriteFitsLine(verbose,fp,"TUNIT2" ,"'hacks '","unit for time"); WriteFitsLine(verbose,fp,"TDISP9" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT9" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP10" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT10" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP13" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT13" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP14" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT14" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP15" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT15" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP16" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT16" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP17" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT17" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP18" ,"'F5.1 '","preferred display format for temperature"); WriteFitsLine(verbose,fp,"TUNIT18" ,"'degC '","unit for temperature"); WriteFitsLine(verbose,fp,"TDISP22" ,"'F6.3 '","preferred display format for HvSet"); WriteFitsLine(verbose,fp,"TUNIT22" ,"'kVolt '","unit for HvSet"); WriteFitsLine(verbose,fp,"TDISP23" ,"'F6.3 '","preferred display format for McpV"); WriteFitsLine(verbose,fp,"TUNIT23" ,"'kVolt '","unit for McpV"); WriteFitsLine(verbose,fp,"TDISP24" ,"'F4.1 '","preferred display format for AnodeV"); WriteFitsLine(verbose,fp,"TUNIT24" ,"'Volt '","unit for AnodeV"); WriteFitsLine(verbose,fp,"TDISP25" ,"'F5.2 '","preferred display format for Strip Current"); WriteFitsLine(verbose,fp,"TUNIT25" ,"'uA '","unit for Strip Current"); WriteFitsLine(verbose,fp,"TDISP26" ,"'F6.3 '","preferred display format for McpV"); WriteFitsLine(verbose,fp,"TUNIT26" ,"'kVolt '","unit for McpV"); WriteFitsLine(verbose,fp,"TDISP27" ,"'F4.1 '","preferred display format for AnodeV"); WriteFitsLine(verbose,fp,"TUNIT27" ,"'Volt '","unit for AnodeV"); WriteFitsLine(verbose,fp,"TDISP28" ,"'F5.2 '","preferred display format for Strip Current"); WriteFitsLine(verbose,fp,"TUNIT28" ,"'uA '","unit for Strip Current"); WriteFitsLine(verbose,fp,"TDISP29" ,"'F6.3 '","preferred display format for McpV"); WriteFitsLine(verbose,fp,"TUNIT29" ,"'kVolt '","unit for McpV"); WriteFitsLine(verbose,fp,"TDISP30" ,"'F5.2 '","preferred display format for Strip Current"); WriteFitsLine(verbose,fp,"TUNIT30" ,"'uA '","unit for Strip Current"); WriteFitsLine(verbose,fp,"TDISP31" ,"'F4.2 '","preferred display format for Discriminator"); WriteFitsLine(verbose,fp,"TUNIT31" ,"'Volt '","unit for Discriminator"); WriteFitsLine(verbose,fp,"TUNIT32" ,"'Hz '","unit for Countrate"); WriteFitsLine(verbose,fp,"TUNIT33" ,"'counts '","unit for Raw LTS"); WriteFitsLine(verbose,fp,"TUNIT34" ,"'counts '","unit for Raw LTS"); WriteFitsLine(verbose,fp,"TUNIT37" ,"'sec '","unit for safety timeout"); WriteFitsLine(verbose,fp,"COMMENT" ,"","- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); WriteFitsLine(verbose,fp,"HKEARLY" ,Flt2Str(realSUTC(hovr->earliest),14,3),"earliest HK packet time (after sync)"); WriteFitsLine(verbose,fp,"HKLATEST",Flt2Str(realSUTC(hovr->latest),14,3),"latest HK packet time"); WriteFitsLine(verbose,fp,"HKENTRS" ,Int2Str(hovr->hkpackets),"total number of HK packets"); WriteFitsLine(verbose,fp,"COMPHK" ,LogicStr[hovr->contin],"complete list flag (after sync)"); WriteFitsLine(verbose,fp,"ORDRHK" ,LogicStr[hovr->order],"incrementing order flag"); WriteFitsLine(verbose,fp,"END" ,"",""); Fill2880(verbose,fp,' '); WriteFitsHkPacketsExt6(verbose,fp,hfil,file,hovr->htf,hovr->hkpackets); fclose(fp); return(1); } else { printf("ERROR: failed to create FITS file (%s)\n",filename); return(0); } } /* () */ /*---- end of file ----------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* LAMP-LIMA: Process raw LRO telemetry files - project 15-11239.02 - SwRI */ /* */ /* $RCSfile: Acquire.c,v $ - $Revision: 1.0 $ - $Date: 2009/02/19 21:58:21 $*/ /*---------------------------------------------------------------------------*/ /* Modification history is at bottom of file DESCRIPTION This file contains a number of general utility functions used in the LAMP-LIMA processing. It also encompasses the 'encapsulated' functionality for the hacktime wrap rollover and least squares hack versus SUTC time correlation and the checksum calculation. ------------------------------------------------------------------------------- MODIFICATION HISTORY $Author: mversteeg $ $State: Exp $ ------------------------------------------------------------------------------- $Log: Acquire.c,v $ -----------------------------------------------------------------------------*/ /* includes -----------------------------------------------------------------*/ /* defines ------------------------------------------------------------------*/ #define CHECKSUMSEED 0xf5f900 #define NOSCIENCE 99999999 // checksum prior to receiving science // data #define WRAPTIMEWRAP 65536 // wrap for 16-bit hack value /* typedefs -----------------------------------------------------------------*/ /* globals ------------------------------------------------------------------*/ /* locals -------------------------------------------------------------------*/ /* Hack counter unwrap variables */ static int HackWrapOffset= 0; // start with hack offset of zero static int LastHackTime= -1; /* Clock - hacktime Least Square Fit global variables */ static int HrSamples= 0; static double HrOffset= 0.0; // initial value for offset (SCUT at // hack=0) static double HrSx= 0.0; // hack static double HrSy= 0.0; // SCUT static double HrSxx= 0.0; static double HrSxy= 0.0; static double HrSyy= 0.0; static int HrLastX= -1; // x (hack) interval validity /* forward declarations -----------------------------------------------------*/ /*-----------------------------------------------------------------------*/ char before(SUTC tim1, SUTC tim2) /* ARGS two SUTC time values in seconds and subseconds representation RETURNS true if time-1 occurs before time-2 */ { return((tim1.sec----------------------------------------------------------------------*/ char after(SUTC tim1, SUTC tim2) /* ARGS two SUTC time values in seconds and subseconds representation RETURNS true if time-1 occurs after time-2 */ { return((tim1.sec>tim2.sec)||((tim1.sec==tim2.sec)&&(tim1.sub>tim2.sub))); } /* after() */ /*-----------------------------------------------------------------------*/ char equalSUTC(SUTC tim1, SUTC tim2) /* ARGS two SUTC time values in seconds and subseconds representation RETURNS true if both values are equal (seconds and sub-seconds) */ { return((tim1.sec==tim2.sec)&&(tim1.sub==tim2.sub)); } /* equalSUTC() */ /*-----------------------------------------------------------------------*/ double realSUTC(SUTC tim) /* ARGS SUTC time in second and sub seconds representation RETURNS SUTC in a real (float) format */ { return(tim.sec+tim.sub/65535.0); } /* realSUTC() */ /*-----------------------------------------------------------------------*/ int validHiLtsEntries(BYTE ltsa[10], BYTE ltsb[10], BYTE timstat, BYTE ltstim[10]) /* ARGS array with up to 10 LTS A values, array with up to 10 LTS B values, HK debug status variable, array with up to 10 LTS time values (debug entries) RETURNS number of valid entries in the LTS array DESCRIPTION The LTS data acquisition is based on the instrument internal 10 Hz clock but the HK generation rate is based on the spacecraft clock based time sync pulse (in nominal configuration). Since these clocks work from different time sources thre might be a slight discrepancy so not all 1Hz cycles 10 samples will be captured for the LTS. This function attemps to determine how many valid entries there are in the 10 entry LTS arrays. If the debug option has been set to include LTS time information in the HK packet then this information will also be used in this determination. Note that this determination looks at the received data there is no explicit indication of the number of valid entries so this determination may be incorrect. */ { int j=9; if (timstat==0xf2) { // lts time available, only discard complete zero entries when time // is unexpected zero (expected zero of previuos time 100 ms earlier). while ( (j>0)&&(ltsa[j]==0)&&(ltsb[j]==0) &&(ltstim[j]==0)&&((ltstim[j-1]<230)||(ltstim[j-1]>232))) { j--; } } else { // no lts time avilable */ if ((ltsa[0]>0)||(ltsb[0]>0)) { /* discard all the complete zero entries from the end */ while ((ltsa[j]==0)&&(ltsb[j]==0)) { j--; } } } return(j+1); } /* validHiLtsEntries() */ /*-----------------------------------------------------------------------*/ void initializeHkOverview(HKOVERVIEW *hovr) /* ARGS pointer to HK overview structure RETURNS none DESCRIPTION Initialize all values in the HK overview packet, initially all values are set to zero, then the values that need to be initialized to non-zero values are explicitly set to these values. For the overview parameters that track minimum and maximum values the limits are set to extreme values such that when the first HK packet is received these values will replace the initialized values. */ { int i; memset(hovr,0,sizeof(HKOVERVIEW)); hovr->earliest.sec= MAXLONG; hovr->contin= 1; hovr->order= 1; hovr->hvsetmin= 255; hovr->mcpv1min= 255; hovr->anov1min= 255; hovr->stpi1min= 255; hovr->mcpv2min= 255; hovr->anov2min= 255; hovr->stpi2min= 255; hovr->discvmin= 255; hovr->mincntr= 65535; hovr->evcntlo= MAXLONG; hovr->miratmin= 255; hovr->miratsum= 0.0; hovr->mirbtmin= 255; hovr->mirbtsum= 0.0; hovr->grtatmin= 255; hovr->grtatsum= 0.0; hovr->grtbtmin= 255; hovr->grtbtsum= 0.0; hovr->cndhtmin= 255; hovr->cndhtsum= 0.0; hovr->dethtmin= 255; hovr->dethtsum= 0.0; hovr->ltsamin= 255; hovr->ltsasum= 0.0; hovr->ltsbmin= 255; hovr->ltsbsum= 0.0; hovr->ltstime= 1; hovr->ltsentries= 0; for (i=0;ihtf[i++].firsthack= -1); } /* initializeHkOverview() */ /*-----------------------------------------------------------------------*/ void updateHkOverview(CCSDSHEADER ccsds, SUTC stim, BYTE hkpkt[LAMPHKPKTLEN-5] , HKOVERVIEW *hovr) /* ARGS CCSDS header, spacecraft time SUTC, HK packet, pointer to HK overview structure RETURNS none DESCRIPTION Update the HK overview that keeps track of averages, minima, maxima and statechanges of the values in the received HK packet. This overview is kept in the HK overview variable and this function performs an update for all values in this overview based on the latest HK packet. The function will also print a warning when a discontinuity in the HK packets is observed based on the packet count */ { static int hkpktcnt= -1; unsigned j,entries; if ((hkpktcnt!=-1)&&(ccsds.pktcnt!=((hkpktcnt+1)&0x3fff))) { printf("WARNING: HK packet count discontinuity %d after %d (SCUT %14.3f)\n" ,ccsds.pktcnt,hkpktcnt,realSUTC(stim)); hovr->contin= 0; } hkpktcnt= ccsds.pktcnt; // HK overview parameter acquistions hovr->hkpackets++; // determine earliest and latest but exclude no sync // also check order of all received packets. if (stim.sec>SYNCHEDTIME) { if (before(stim,hovr->earliest)) { memcpy(&hovr->earliest,&stim,sizeof(SUTC)); } if (after(stim,hovr->latest)) { memcpy(&hovr->latest,&stim,sizeof(SUTC)); } if (before(stim,hovr->latest)) { hovr->order= 0; } } // capture all state changes, durations and limits statechange(hovr->tsafety,1+((hkpkt[79]&0x20)>>5)); statechange(hovr->csafety,1+((hkpkt[79]&0x10)>>4)); statechange(hovr->asafety,1+((hkpkt[79]&0x08)>>3)); statechange(hovr->ssafety,1+((hkpkt[79]&0x04)>>2)); statechange(hovr->hsafety,1+((hkpkt[79]&0x02)>>1)); statechange(hovr->bsafety,1+(hkpkt[79]&0x01)); hovr->lastsafe= ((hkpkt[0]&0x07)>0)?hkpkt[0]&0x07:hovr->lastsafe; statechange(hovr->lvpsstate,1+((hkpkt[1]&0xc0)>>6)); statechange(hovr->hvpsstate,1+(((hkpkt[1]&0x30)>>4)&((hkpkt[1]&0xc0)>>6))); statechange(hovr->ltsstate,1+((hkpkt[16]&0x04)>>2)); statechange(hovr->apdstate,1+((hkpkt[16]&0x30)>>4)); minmax(hkpkt[20],hovr->hvsetmin,hovr->hvsetmax); minmax(hkpkt[36],hovr->mcpv1min,hovr->mcpv1max); minmax(hkpkt[37],hovr->anov1min,hovr->anov1max); minmax(hkpkt[38],hovr->stpi1min,hovr->stpi1max); minmax(hkpkt[39],hovr->mcpv2min,hovr->mcpv2max); minmax(hkpkt[40],hovr->anov2min,hovr->anov2max); minmax(hkpkt[41],hovr->stpi2min,hovr->stpi2max); // Find operational HV level if HvSet parameter is reported if ((hkpkt[106]==13)&&(hkpkt[107]>0)) // paramindex == P_HvLevel { hovr->hvopslvl= hkpkt[107]; // paramvalue (last reported) } minmax(hkpkt[44],hovr->discvmin,hovr->discvmax); minmax((hkpkt[18]*256+hkpkt[19]),hovr->mincntr,hovr->maxcntr); minmax(((LONG)hkpkt[21]*65536+hkpkt[22]*256+hkpkt[23]),hovr->evcntlo,hovr->evcnthi); hovr->evcounts+= (hkpkt[18]*256+hkpkt[19]); minmax(hkpkt[73],hovr->miratmin,hovr->miratmax); hovr->miratsum+= TempsCal(hkpkt[73]); minmax(hkpkt[74],hovr->mirbtmin,hovr->mirbtmax); hovr->mirbtsum+= TempsCal(hkpkt[74]); minmax(hkpkt[75],hovr->grtatmin,hovr->grtatmax); hovr->grtatsum+= TempsCal(hkpkt[75]); minmax(hkpkt[76],hovr->grtbtmin,hovr->grtbtmax); hovr->grtbtsum+= TempsCal(hkpkt[76]); minmax(hkpkt[77],hovr->cndhtmin,hovr->cndhtmax); hovr->cndhtsum+= TempsCal(hkpkt[77]); minmax(hkpkt[78],hovr->dethtmin,hovr->dethtmax); hovr->dethtsum+= TempsCal(hkpkt[78]); statechange(hovr->mirhtrstate,1+((hkpkt[2]&0x0c)>>2)); statechange(hovr->grthtrstate,1+(hkpkt[2]&0x03)); statechange(hovr->detdstate,1+((hkpkt[16]&0xc0)>>6)); statechange(hovr->stimstate,1+(hkpkt[17]&0x01)); hovr->swvers= hkpkt[84]; hovr->hwvers= (hkpkt[83]&0x0f); hovr->swchck= hkpkt[86]*256+hkpkt[87]; entries= validHiLtsEntries(&hkpkt[50],&hkpkt[60] ,hkpkt[104],&hkpkt[93]); hovr->ltsentries+= entries; // count total # of entries for(j=0;jltsamin,hovr->ltsamax); hovr->ltsasum+= hkpkt[50+j]; minmax(hkpkt[60+j],hovr->ltsbmin,hovr->ltsbmax); hovr->ltsbsum+= hkpkt[60+j]; } if (hkpkt[104]!=0xf2) // lts time not available { hovr->ltstime= 0; } } /* updateHkOverview() */ /*-----------------------------------------------------------------------*/ void resetHackWrap() /* ARGS none RETURNS none DESCRIPTION Reset the internal variable that is used to keep track of how many times the hack clock has wrapped around in the current series of HK packets. This internal variable is (will be) reset every time when the series of HK packets is rescanned. */ { HackWrapOffset= 0; LastHackTime= -1; } /*-----------------------------------------------------------------------*/ int unwrappedHackTime(BYTE hk24, BYTE hk25) /* ARGS bytes 24 and 25 of a HK packet (hacktime) RETURNS unwrapped hack time value DESCRIPTION Calculate an unwrapped hack time value from HK data. This unwrapped hacktime ensures that the 16-bit hack clock which basically wraps around after 65 sec is extended so that the same hack value never appears in a series of HK packets. Higher order bits are appended each time the hack clock value rolls over. Thus unwrapping the time series and allowing a linear fit between the unwrapped hack time and the spacecraft clock time. */ { int hacktime= hk24*256+hk25; if (hacktime----------------------------------------------------------------------*/ double flabs( double a ) /* ARGS double value RETURNS absolute value of the input value */ { if (a<0.0) { a= -a; } return(a); } /* flabs() */ /*-----------------------------------------------------------------------*/ int calculateHackrate(int hackinterval) /* ARGS difference between two hack times RETURNS Calculate a hackrate from the difference between two hack times. The calculated hackrate will start with 1 indicating a 4 ms rate and run upto 8 for a 512 ms hackrate. The calculated hackrate will be verified for range and a proper power of two and the function will print an error and return -1 if no proper interval was specified. */ { int hackrate= 0; int ht; hackinterval= (hackinterval+0x8000)%0x8000; ht= hackinterval; while(ht>0) { hackrate++; ht>>= 1; } if (hackrate>8) { printf("ERROR: incorrect hackrate %d, limited to 8\n",hackrate); hackrate= -1; } if (hackinterval!=(1<<(hackrate-1))) { printf("ERROR: incorrect hackinterval %d, must be power of 2\n",hackinterval); hackrate= -2; } return(hackrate); } /* calculateHackrate() */ /*-----------------------------------------------------------------------*/ void updateClockStatistics(double x, double y) /* ARGS extended hack value, sutc value RETURNS none DESCRIPTION Update the calculated statistics for a linear fit calculation, but only if the SUTC value indicates a synchronized time value, meaning the SUTC fine is larger than a defined start mission value. To increase the accuracy of the calculation by limiting error caused by manipulating very large numbers a first order approximation of the offset is used given by the SUTC value of the first value pair, this is subtracted from all further supplied SUTC values as a pre-correction and is corrected for in the final least squares fit calculation. The function also updates the last-used hack time value to define the interval for which this linear fit is to be used. */ { if (y>SYNCHEDTIME) // calculate only for synched packet { HrSamples++; if (HrSamples==1) // only for first pair of a fit { HrOffset= y; } y= y-HrOffset; // pre-correct for offset HrSx+= x; HrSy+= y; HrSxx+= x*x; HrSxy+= x*y; HrSyy+= y*y; } HrLastX= (int)x; // last used hack value to keep track // of valid hack range } /* updateClockStatistics() */ /*-----------------------------------------------------------------------*/ void calculateClockLeastSquaresFit(HACKTIMEFRAME *htf) /* ARGS single hacktime frame correlation entry RETURNS none DESCRIPTION Calculate a least squares fit based on the gathered statistics between the hacktime and the SCUT, normally there will only be a single correlation but when the data includes jumps a single dataset may result in a number of linear approximation relations for the time correlation. This calculation takes into account the offset correction that was applied during the statistics gathering. At the end of this function the correlation statistics are reset so the statistics calculation is ready to start gathering statistics foir the next segment. If no samples are present, the reported hackrate will be set to the default rate, this will also happen when the divider in the rate calculation term is too small (less than EPS). If a properly calculated hackrate still shows too large a deviation from the nominally expected rate, a warning is printed and the calculated hackrate is replaced again by the nominal hackrate. */ { if (HrSamples>=1) { htf->hackoffset= HrOffset+(HrSy*HrSxx-HrSx*HrSxy)/(HrSamples*HrSxx-HrSx*HrSx); if (flabs(HrSamples*HrSyy-HrSy*HrSy)hackrate= NOMINALHACKCLOCK; htf->hackcorr= 1.0-sqrt(EPS); } else { htf->hackrate= (HrSamples*HrSyy-HrSy*HrSy)/(HrSamples*HrSxy-HrSy*HrSx); htf->hackcorr= HrSxy/sqrt(HrSxx*HrSyy); if (flabs((htf->hackrate/NOMINALHACKCLOCK)-1.0)>MAXHACKDEVIATION) { printf("WARNING: calculated hackrate deviates more than %3.1f%% (nom:%5.3f ms, calc:%5.3f ms)%s\n" ,100*MAXHACKDEVIATION,1000*NOMINALHACKCLOCK,1000*(htf->hackrate),", revert to default value"); htf->hackrate= NOMINALHACKCLOCK; } } } else { htf->hackoffset= -1.0; htf->hackrate= NOMINALHACKCLOCK; htf->hackcorr= 0.0; } htf->lasthack= HrLastX; /* reset statist calculations */ HrSamples= 0; HrOffset= 0.0; // initial guess for offset (SCUT at hack=0) HrSx= 0.0; // hack HrSy= 0.0; // SCUT HrSxx= 0.0; HrSxy= 0.0; HrSyy= 0.0; HrLastX= -1; } /* calculateClockLeastSquaresFit() */ /*-----------------------------------------------------------------------*/ double reconstructedSUTC(int exthack, HACKTIMEFRAME htf[MAXTIMEFRAMES]) /* ARGS hacktime, hack time correlation array number of frames in use RETURNS corresponding SCUT DESCRIPTION Determine the corresponding SUTC for a given hacktime based on the linear correlation(s) specified in the time segments array. The array stores entries in order of increasing hacktimes so a simple linear lookup for the entry can be used. */ { int i= 0; while ((ihtf[i].lasthack)) { i++; } if (htf[i].lasthack==0) { if (i>0) { i--; } } return(htf[i].hackoffset + htf[i].hackrate * exthack); } /* reconstructedSUTC() */ /*-----------------------------------------------------------------------*/ LONG reconstructedHack(double scut, HACKTIMEFRAME htf[MAXTIMEFRAMES]) /* ARGS time scut, hack time correlation array number of frames in use RETURNS corresponding hack time DESCRIPTION Determine the corresponding hacktime for a specified SCUT time based on the linear approximation(s) stored in the time segments array. Note that the time segments are not ordered by SCUT, so just do a search for the time frame including the specified SCUT. */ { // int i= 0; while ( (i(htf[i].hackoffset+(htf[i].lasthack * htf[i].hackrate))) &&(scut<(htf[i].hackoffset+(htf[i].firsthack * htf[i].hackrate))) ) { i++; } if (i----------------------------------------------------------------------*/ LONG updateChecksum(LONG chk, LONG w) /* ARGS current checksum value, word (16-bits) to be added RETURNS updated checksum DESCRIPTION Calculate an updated value of the word-wise (16-bits) calculated 24-bit XOR rotate checksum. */ { if (w>0xffff) { w= (w&0xffff)^(w>>16); } chk= (((chk&0x7fffff)<<1)+(chk>>23))^w; return(chk); } /*-----------------------------------------------------------------------*/ BYTE scienceType(LONG checksum) /* ARGS calculated frame checksum RETURNS one byte code indicating the file contents matching the specified checksum DESCRIPTION Perform a file data contents recognition by comparing the calculated 24-bit checksum against a list of know checksums for 13 different types of test patterns. If no matching checksum is found the code 1 is returned indicating a data file. Note that this method is not perfect and will fail by reporting a test pattern on average once in about every 1.3 million frames even though the frame for which the checksum was calculated was actually a data file. */ { // Note ensure that test pattern checksums remain consistent with // checksums specified in the FrameCheck program: // Cnst, IncL, IncH, DecL, DecH, Hnst, Hstm, P004, P008..P512 LONG checks[15]= {0,NOSCIENCE,0xcd43bb,0x422460,0x825b9f,0xc96da2 ,0x09125d,0x408a75,0x8c84e1,0x65eefa ,0x2ee87c,0,0,0 ,0x83b212}; BYTE i= (sizeof(checks)/sizeof(LONG))-1; while ((i>0)&&(checks[i]!=checksum)) { i--; } return(i); } /* scienceType() */ /*-----------------------------------------------------------------------*/ char* currentUtcTimeStr(char str[]) /* ARGS string for result, must be at least 25 characters long RETURNS pointer to time string DESCRIPTION Return the current time in a string time in UTC format: YY-MM-DDThh:mm:ss (with hours in a 24 hour format) and return a pointer to this string, the caller has to provide a string for the storage of the result of sufficient size (at least 25 bytes). */ { time_t ltime; time( <ime ); strftime(str,25,"'%Y-%m-%dT%H:%M:%S'",gmtime(<ime)); return(str); } /* currentTimeStr() */ /*-----------------------------------------------------------------------*/ char* utcTimeStr(SUTC tim) /* ARGS SUTC time RETURNS pointer to time string DESCRIPTION Convert a time structure into a string time in UTC format: YY-MM-DDThh:mm:ss (with hours in a 24 hour format) and return a pointer to this string. The string itself is kept in a static internal storage, so the function is non-reentrant, and the caller has to ensure that the function is only called once before the result is being used. */ { static char str[30]; struct tm lrooffset= LROEPOCH; tim.sec+= mktime(&lrooffset); #ifdef WIN32 _daylight= 0; // No daylight savings time in UTC #else daylight= 0; #endif strftime(str,25,"%Y-%m-%dT%H:%M:%S",localtime(&tim.sec)); sprintf(&str[strlen(str)],".%03d",(tim.sub*1000)/65536); return(str); } /* utcTimeStr() */ /*-----------------------------------------------------------------------*/ char* strCopyN(char res[], char str[], int n) /* ARGS result string, input string, number of characters RETURNS pointer to result string DESCRIPTION copy upto the specified number of characters from the input string, terminate the resulting string with a null character. */ { int i= 0; while ((n-->0)&&(*str!='\0')) { res[i++]= *(str++); } res[i]= '\0'; return(res); } /* strCopyN() */ /*-----------------------------------------------------------------------*/ int FileExists(char name[]) /* ARGS filename RETURNS 1 - file exists DESCRIPTION Check whether a file with the given name already exists, by trying to open the specified file in read mode. */ { FILE *fp; int exist; exist= ((fp=fopen(name,"r"))!=NULL); if (exist) { fclose(fp); } return(exist); } /* FileExists() */ /*-----------------------------------------------------------------------*/ int FileEmpty(FILE *fp) /* ARGS file pointer RETURNS 0 - non-empty file or non-existing file, 1 - empty file DESCRIPTION Check whether the specified file is empty by moving the file pointer to the end and checking the reported poisition. */ { int pos; if (fseek(fp,0,SEEK_END)!=0) { printf("fseek failed to move to end of file\n"); } pos = ftell(fp); if (pos<0) { printf("FAILED: to get position information from file\n"); return(0); } else if (pos>0) /* file already contains data */ { return(0); } else { return(1); } } /* FileEmpty() */ /*-----------------------------------------------------------------------*/ void readLONG(FILE *fp, LONG *lng) /* ARGS file pointer, pointer to long (32-bits) RETURNS nonw DESCRIPTION Read a single long (32-bit) from the input file, reading the four bytes in normal order */ { int i; *lng= 0; for (i=0;i<4;i++) { *lng=((*lng)<<8)+fgetc(fp); } } /* readLONG() */ /*-----------------------------------------------------------------------*/ void readWORD(FILE *fp, WORD *lng) /* ARGS file pointer, pointer to word (16-bits) RETURNS nonw DESCRIPTION Read a single word (16-bit) from the input file, reading the two bytes in normal order */ { *lng= getc(fp)<<8; *lng+= getc(fp); // printf("- 0x%04x\n",*lng); } /*-----------------------------------------------------------------------*/ void readDROW(FILE *fp, WORD *lng) /* ARGS file pointer, pointer to word (16-bits) RETURNS nonw DESCRIPTION Read a single word (16-bit) from the input file, reading the two bytes in reverse order, needed for instrument science data */ { *lng= fgetc(fp); *lng+= fgetc(fp)<<8; // note two codes below compile different for Release and Debug !! // *lng= fgetc(fp)+(fgetc(fp)<<8); // versus // *lng= fgetc(fp); // *lng+= fgetc(fp)<<8; // printf("x 0x%04x\n",*lng); } /* readDROW() */ /*-----------------------------------------------------------------------*/ void readString(FILE *fp, char *str, int len) /* ARGS file pointer, pointer to result string, number of characters to read RETURNS none DESCRIPTION Read a single string of specified length from the input file, the function just reads the input file, no additional string termination is added. */ { int i; for (i=0;i----------------------------------------------------------------------*/ void readLine(FILE *fp, char *str, int max) /* ARGS file pointer, pointer to result string, maximum number of characters to read RETURNS none DESCRIPTION Read a single string from the input file, first the leading blank characters (including end of line) are skipped, then up to the maximum specified number of characters are read till an end of line is found. The returned string will be terminated by a null character. */ { int i= 0; char ch= '\n'; while ((ch=='\n')||(ch=='\r')||(ch==' ')) { ch= fgetc(fp); } while ((!feof(fp))&&(ch!='\n')&&(ch!='\r')) { if (i----------------------------------------------------------------------*/ char readFileHeader(FILE *fp, FILEHEADER *hdr) /* ARGS file pointer, pointer to header data structure RETURNS eof status DESCRIPTION Read a complete 32 word (16 bit) LRO telemetry header data structure from the specified input file and return the filled header data structure, the function return status is based on the end of file status at the end of the 32 word read. */ { WORD dummy; readLONG(fp,&(hdr->FileTypeID)); readLONG(fp,&(hdr->Spare)); readLONG(fp,&(hdr->Start.sec)); readWORD(fp,&dummy); readWORD(fp,&(hdr->Start.sub)); readLONG(fp,&(hdr->Stop.sec)); readWORD(fp,&dummy); readWORD(fp,&(hdr->Stop.sub)); readString(fp,hdr->FileName,40); return(!feof(fp)); } /* readFileHeader() */ /*-----------------------------------------------------------------------*/ char readCcsdsHeader(FILE *fp, CCSDSHEADER *ccsds) /* ARGS file pointer, pointer to CCSDS data structure RETURNS eof status DESCRIPTION Read a complete 3 word (16-bit) CCSDS header structure from the specified input file and return the filled CCSDS data structure, the function return status is based on the end of file status at the end of the 3 word read. */ { WORD w; readWORD(fp,&w); ccsds->pktvno= (w & 0xc000)>>12; ccsds->pcktyp= (w & 0x2000)>>11; ccsds->shdf= (w & 0x1000)>>10; ccsds->apid= (w & 0x0fff); readWORD(fp,&w); ccsds->segflg= (w & 0xc000)>>12; ccsds->pktcnt= (w & 0x3fff); readWORD(fp,&(ccsds->pktlen)); return(!feof(fp)); } /* readCcsdsHeader() */ /*-----------------------------------------------------------------------*/ void skipBYTEs(FILE *fp, int n) /* ARGS file pointer, number of bytes RETURNS none DESCRIPTION Skip the specified number of bytes form the input file, function simply performs the specified number of getc() calls without verifying whether the end of file of the input file is reached. */ { int i,c; for (i=0;i----------------------------------------------------------------------*/ char* skipPath(char str[]) /* ARGS filename RETURNS finame with directory path removed DESCRIPTION Function returns a pointer to the position in the input string where the actual file name starts, it removes all directory path infornmation from the filename. */ { char *si; char *s= str; if ((si=strrchr(s,'/'))!=NULL) { s= si+1; } if ((si=strrchr(s,'\\'))!=NULL) { s= si+1; } return(s); } /* skipPath() */ /*-----------------------------------------------------------------------*/ void makeOutputFileName(LONG tim, char dir[], char ext[], char name[]) /* ARGS time (SCUT), directory path, extension ('fit' or 'txt'), filename (space for generated name) RETURNS none DESCRIPTION Generate a main output file name, based on start time and extension, the format of the generated name is: LAMP_ENG_tttttttttt_vv.ext with: - tttttttttt - SCUT in seconds - vv - version number starting at 01 - ext - FIT for main FITS file TXT for engineering file */ { int i= 0; // increment version number if file already exists do { sprintf(name,"%s/%s%010d_%02d.%s",dir,LAMPENGBASENAME,tim,++i,ext); } while ((FileExists(name))&&(i<99)); } /* makeOutputFileName() */ /*-----------------------------------------------------------------------*/ void makeMemDumpFileName(LONG tim, char dir[], BYTE memtyp, LONG addr, char name[]) /* ARGS time (SCUT), directory path, memory type (0x50-0x56), filename (space for generated name) RETURNS none DESCRIPTION Generate a memory dump data file name, based on start time, memory type and start address, the format of the generated name is: LAMP_ENG_tttttttttt_vv_mmxxxx.BIN with: - tttttttttt - SCUT in seconds - vv - version number starting at 01 - mm - memory type RA, E1, E2, E3, E4, AM or PR - xxxx - start address in hex */ { char memstr[8][3]= {"XX","RA","E1","E2","E3","E4","AM","PR"}; int i= 0; if ((memtyp<0x50)||(memtyp>0x56)) { memtyp= 0; } else { memtyp-= (0x50-1); } // increment verion number if file already exists do { sprintf(name,"%s/%s%010d_%02d_%s%04x.%s",dir,LAMPENGBASENAME,tim,++i ,memstr[memtyp],addr,MEMEXTEND); } while ((FileExists(name))&&(i<99)); } /* makeMemDumpFileName() */ /*-----------------------------------------------------------------------*/ void makeOutputScFileName(LONG tim, char dir[], char typ[], char name[]) /* ARGS time (SCUT), directory path, filename (space for generated name) RETURNS none DESCRIPTION Generate a spacecraft HK output file name, based on start time and type, the format of the generated name is: LAMP_ENG_tttttttttt_type.TXT with: - tttttttttt - SCUT in seconds of the first packet - vv - version number starting at 01 - type - 'temps', 'power' or 'swim' */ { int i= 0; // increment version number if file already exists do { sprintf(name,"%s/%s%010d_%02d_%s.txt",dir,LAMPENGBASENAME,tim,++i,typ); } while ((FileExists(name))&&(i<99)); } /* makeOutputFileName() */ /*-----------------------------------------------------------------------*/ void makeMemDataFileName(LONG tim, char filtyp[], char dir[], char name[]) /* ARGS time (SCUT), file type ('errlog', 'params' or 'adlife'), directory path, name (space for generated name) RETURNS none DESCRIPTION Generate a memory data product file name, based on the time and product, the format of the generated name is: LAMP_ENG_tttttttttt_vv_prodct.TXT with: tttttttttt - SCUT in seconds vv - version number starting at 01 prodct - 'errlog', 'params' or 'adlife' */ { int i= 0; // increment version number if file already exists do { sprintf(name,"%s/%s%010d_%02d_%s.%s",dir,LAMPENGBASENAME,tim,++i,filtyp ,TXTEXTEND); } while ((FileExists(name))&&(i<99)); } /* makeMemDataFileName() */ /*-----------------------------------------------------------------------*/ char findTarget(double t0, char targetfile[], char target[], char activity[]) /* ARGS time, target file including path RETURNS target name DESCRIPTION Find a target based on the specified activity time and the (text) targetfile. The target file contains a list of time periods that specify 'targets' for that period, target should be interpreted in a wide sense, target file may also specify a specific period of operations like 'TVac' or 'commissioning_phase_104' which covers the parameter file verification. The first 'target' defined for which the specified time falls in the time range will be returned. This allows for specifying precise targets in the file followed by more general phases, so the precise specification will override the more general period. */ { FILE *fp; char line [80]; double start,stop; int n; char name1[40]= ""; char name2[40]; if ((fp = fopen(targetfile,FMODEREAD))!=NULL) { while (!feof(fp)) { readLine(fp,line,80); if ((strlen(line)>0)&&(line[0]!='#')) { if ((n=sscanf(line,"%lf %lf %30s %30s",&start,&stop,name1,name2))>=3) { if ((t0>=start)&&(t0<=stop)) { if (target!=NULL) { strcpy(target,name1); } if (activity!=NULL) { if (n>3) { strcpy(activity,name2); } else { strcpy(activity,"none"); } } return(1); } } else { printf("ERROR: format error in target file: %s\n",line); return(0); } } } /* while !eof() */ fclose(fp); } if (target!=NULL) { strcpy(target,NOTARGETSTR); } if (activity!=NULL) { strcpy(activity,"none"); } return(0); } /* findTarget() */ /*-----------------------------------------------------------------------*/ char* CommandStr(int cmd) /* ARGS command code RETURNS LAMP command name string DESCRIPTION Get LAMP command mnemonic from a LAMP command code, the function returns the LRO defined LAMP command name. */ { static char cmdstr[27][20]= {"none","LANOOP","LASAFEMODE","LACHKOUTMODE" ,"LACRITCONF","LASTARTHISTGRM","LASTARTPIXELLST" ,"LAPARAMSET","LAPARAMSWRIT","LAPARAMSLD" ,"LAACTPIXSTIM","LADEACTPIXSTIM","LADISCSET" ,"LAAPDORCLS","LAHVPSDEACT","LAAPDOROPN" ,"LAHVPSACT","LAHTRCTRL","LAWPASMAACT","LASELFTEST" ,"LAPRGSTRT","LAREQTURNOFF","LARSTTCSTAT" ,"LAMEMCHK","LAMEMLD","LAMEMDMP"}; if ((cmd>=0)&&(cmd<=25)) { return(cmdstr[cmd]); } else if (cmd==0xff) { return("reset"); } else { return("unknown"); } } /* CommandStr() */ /*---- end of file ----------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* LAMP-LIMA: Process raw LRO telemetry files - project 15-11239.02 - SwRI */ /* */ /* $RCSfile: Acquire.c,v $ - $Revision: 1.0 $ - $Date: 2009/02/19 21:58:21 $*/ /*---------------------------------------------------------------------------*/ /* Modification history is at bottom of file DESCRIPTION This file contains all the general defined and constants used in the LAMP-LIMA program. Also included are a number of tyep definitions that define the data structures used in the program. ------------------------------------------------------------------------------- MODIFICATION HISTORY $Author: mversteeg $ $State: Exp $ ------------------------------------------------------------------------------- $Log: Acquire.c,v $ -----------------------------------------------------------------------------*/ /* includes -----------------------------------------------------------------*/ /* defines ------------------------------------------------------------------*/ #define VERSIONSTR "LAMP-LIMA, version 0.29, date 2009-09-28" /* hardcoded offsets in version string used, */ /* so format should not be changed */ // "LAMP-LIMA, version #.##, date yyyy-mm-dd" #define LROEPOCH { 0, 0, 0, 1, 0, 101 } // offset for January 1, 2001 00:00:00 // LRO TM epoch, defined in 431-HDBK- // 000052 rev B (8/11/2007) page 30 #define SYNCHEDTIME 2000000 /* assume time is synched if reported after */ #define NOMINALHACKCLOCK 0.004 /* 4 ms per hack tick */ #define MAXHACKSTEP 0.512 /* larger than largest nominal hack step */ #define MAXHACKCORRECTION 5.0 /* max correction of pixel hack based timing */ #define MAXCLOCKMISMATCH 1.0 /* max packet clock hack time mismatch */ #define HSDATATIMELIMIT 1.5 /* max allowed HS data capture after hack */ #define MAXHACKDEVIATION 0.01 /* max deviation from nominal hack clock */ #define LAMPENGBASENAME "LAMP_ENG_" #define FITEXTEND "fit" /* extension for generated FITS files */ #define TXTEXTEND "txt" /* extension for generated ascii data files */ #define MEMEXTEND "bin" /* extension for generated memory dump files */ #define LOGEXTEND "log" /* extension for generated ascii log files */ #define ERRLOGNAME "errlog" #define PARAMSNAME "params" #define ADLIFENAME "adlife" #define TARGETFILE "lamp_targets.txt" /* default target filename */ #define NOTARGETSTR "NO-TARGETS" #define MAXCALTABELLEN 40 /* maximum calibration table length */ #define EPS 0.000000000001 #ifdef WIN32 /* Windoze file access modes */ #define FMODEREAD "rb" #define FMODEWRITE "wb" #define FMODEAPPEND "ab" #else /* Normal file acces modes */ #define FMODEREAD "r" #define FMODEWRITE "w" #define FMODEAPPEND "a" #endif #define minmax(c,min,max) if(cmax){max=c;} #define statechange(os,ns) os= (((os)==0)?(ns):(((os)==(ns))?os:5)) #define SCIFILETYPEID 220 // 0xdc #define HKFILETYPEID 221 // 0xdd #define LAMPHKAPID 129 #define LAMPHKPKTLEN 115 // CCSDS length: secondary header + data -1 #define LAMPMDAPID 130 #define LAMPMDPKTLEN 141 // CCSDS length: secondary header + data -1 #define LAMPPARAMETERS 71 // number of used parameter file entries #define MAXLROPKTLEN 1750 // Maximum LAMP used LRO packet size #define LROTEMPAPID 0x6a // LRO Temperature Packet ApId (=106) #define LROTEMPPKTLEN 601 // CCSDS length: secondary header + data -1 #define LROTEMPFILTYP "temps" #define LROPOWRAPID 0x64 // LRO Power Packet ApId (=100) #define LROPOWRPKTLEN 305 // CCSDS length: secondary header + data -1 #define LROPOWRFILTYP "power" #define LROSWIMAPID 0x20 // LRO Instrument Manager Packet ApId (=32) #define LROSWIMPKTLEN 321 // CCSDS length: secondary header + data -1 #define LROSWIMFILTYP "swim" #define LROSWIDAPID 0x30 // LRO SWIM debug Packet ApId (=48) #define LROPWRDAPID 0x65 // LRO Power Debug Packet ApId (=101) #define MAXLONG 0xffffffff #define MAXFILES 250 #define MAXFILENAME 120 #define MAXDIRNAME 120 #define MAXACQUISITIONS 10000 // #define MAXTIMEFRAMES 20 // time correction offset and // Access bit/byte fields out of the HkPkt (byte array) #define HkPktBit(a,n) ((hkpkt[a]>>n)&0x01) #define HkPktBits(a,n,m) ((hkpkt[a]>>n)&m) #define HkPktWrd12(a) (((hkpkt[a]&0xf)<<8)+hkpkt[a+1]) #define HkPktWrd16(a) ((hkpkt[a]<<8)+hkpkt[a+1]) #define HkPktWrd24(a) ((hkpkt[a]<<16)+(hkpkt[a+1]<<8)+(hkpkt[a+2]) #define HkPktWrd32(a) ((hkpkt[a]<<24)+(hkpkt[a+1]<<16)+(hkpkt[a+2]<<8)+hkpkt[a+3]) /* typedefs -----------------------------------------------------------------*/ typedef unsigned char BYTE; /* 8 bits */ typedef unsigned short WORD; /* 16 bits */ typedef unsigned long LONG; /* 32 bits */ typedef struct SUTC { LONG sec; WORD sub; } SUTC; typedef struct FILEHEADER { LONG FileTypeID; LONG Spare; SUTC Start; SUTC Stop; char FileName[40]; char realname[MAXFILENAME]; } FILEHEADER; typedef struct CCSDSHEADER { WORD pktvno: 3; WORD pcktyp: 1; WORD shdf: 1; WORD apid: 11; WORD segflg: 2; WORD pktcnt: 14; WORD pktlen; } CCSDSHEADER; typedef LONG IMAGE[32768]; // actually [32][1024], but only accessed as a linear array typedef struct HACKTIMEFRAME { double hackrate; // scut = hackoffset + hackrate * hackcount double hackoffset; double hackcorr; int lasthack; // last corrected hack value in this frame int firsthack; // first used hack value in this frame } HACKTIMEFRAME; typedef struct HKOVERVIEW { LONG hkpackets; SUTC earliest; SUTC latest; BYTE contin; BYTE order; BYTE tsafety; BYTE csafety; BYTE asafety; BYTE ssafety; BYTE hsafety; BYTE bsafety; BYTE lastsafe; BYTE lvpsstate; BYTE hvpsstate; BYTE ltsstate; BYTE apdstate; BYTE hvsetmin; BYTE hvsetmax; BYTE mcpv1min; BYTE mcpv1max; BYTE anov1min; BYTE anov1max; BYTE stpi1min; BYTE stpi1max; BYTE mcpv2min; BYTE mcpv2max; BYTE anov2min; BYTE anov2max; BYTE stpi2min; BYTE stpi2max; BYTE hvopslvl; BYTE discvmin; BYTE discvmax; WORD mincntr; WORD maxcntr; LONG evcntlo; LONG evcnthi; LONG evcounts; BYTE miratmin; BYTE miratmax; double miratsum; BYTE mirbtmin; BYTE mirbtmax; double mirbtsum; BYTE grtatmin; BYTE grtatmax; double grtatsum; BYTE grtbtmin; BYTE grtbtmax; double grtbtsum; BYTE cndhtmin; BYTE cndhtmax; double cndhtsum; BYTE dethtmin; BYTE dethtmax; double dethtsum; BYTE mirhtrstate; BYTE grthtrstate; BYTE detdstate; BYTE stimstate; BYTE hwvers; BYTE swvers; WORD swchck; int timeframes; HACKTIMEFRAME htf[MAXTIMEFRAMES+1]; BYTE ltsamin; double ltsasum; BYTE ltsamax; BYTE ltsbmin; double ltsbsum; BYTE ltsbmax; BYTE ltstime; LONG ltsentries; } HKOVERVIEW; typedef struct SCIOVERVIEW { LONG hisacq; // total number of science histograms LONG pixacq; // total number of science pixellist frames LONG dopixacq; // statistics for door open histogram LONG doevts; // based on (accumulated) histogram LONG domincnt; // based on (accumulated) histogram LONG domaxcnt; LONG dohacks; // based on science data double dotime; // based on histo: hk, pixel: sci LONG domaxcr; // based on hk data LONG domincr; LONG dochecksum; // based on (accumulated) histogram BYTE dolts; // based on hk data BYTE dostims; BYTE dohvps; LONG dcpixacq; // statistics for door close histogram LONG dcevts; // based on (accumulated) histogram LONG dcmincnt; // based on (accumulated) histogram LONG dcmaxcnt; LONG dchacks; // based on science data double dctime; // based on histo: hk, pixel: sci LONG dcmaxcr; // based on hk data LONG dcmincr; LONG dcchecksum; // based on (accumulated) histogram BYTE dclts; // based on hk data BYTE dcstims; BYTE dchvps; LONG hacks; // for countrate data from pixellist (science) LONG hackfixes; // total number of hack fixes applied double crtime; // based on science data LONG pixevents; // based on science data LONG mincr; // based on science data LONG maxcr; double timecrdo; // pixellist based LONG evtcrdo; LONG mincrdo; LONG maxcrdo; double timecrdc; // pixellist based LONG evtcrdc; LONG mincrdc; LONG maxcrdc; } SCIOVERVIEW; #define PIXELLIST 0 // HISTOGRAM = 1 #define NOHKHEADER 0x3000 // Special indicator, in scihdr field; normally not // possible as hardware control and last block // don't go together since hardware control // never stops. typedef struct ACQUIT { double gentime; // from science frame header (LRO) WORD header; // from science data (LAMP) LONG checksum; // from science data (LAMP) double start; // from HK TM double stop; // from HK TM WORD scihdr; // from HK TM science header WORD frameno; // from HK TM sci header word BYTE apdstate; // from HK TM (during acquisition) BYTE ltsstate; // from HK TM (during acquisition) BYTE stimstate; // from HK TM (during acquisition) BYTE hvpsstate; // from HK TM (during acquisition) BYTE hackrate; // from HK TM (at acquisition end) WORD mincr; // from HK TM (during acquisition) WORD maxcr; // from HK TM (during acquisition) int hackstart; // histo: from TM, pixel from TM+science int hackstop; // histo: from TM, pixel from TM+science } ACQUIT; typedef struct ACQLIST { int acquisitions; int hisacq; int pixacq; BYTE contin; ACQUIT ac[MAXACQUISITIONS+1]; // use sentinel to set 'unknown' values. } ACQLIST; /* globals ------------------------------------------------------------------*/ /* locals -------------------------------------------------------------------*/ /* forward declarations -----------------------------------------------------*/ /*---- end of file ----------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* LAMP-LIMA: Process raw LRO telemetry files - project 15-11239.02 - SwRI */ /* */ /* $RCSfile: Acquire.c,v $ - $Revision: 1.0 $ - $Date: 2009/02/19 21:58:21 $*/ /*---------------------------------------------------------------------------*/ /* Modification history is at bottom of file DESCRIPTION This file is the central part of the LAMP-LIMA code, including all the files needed to compile the complete LAMP-LIMA module. The function main() in this module activates the complete processing for one or more sets HK and/or SCI files received from the LAMP instrument on board of the LRO orbiter and/or processing of the LAMP related LRO HK files. The program is completely command line driven and all options and input parameters are specified on the command line. The main output of the LAMP-LIMA program is a single FITS file that contains all the information of the LAMP input telemetry files. For some parameters engineering values are included in the output file but the original data are included so no data are lost. The conversion factors for these engineering calibrations are kept in the file lal-cali.h and compiled into the executable. This is not a problem since these engineering calibration factors are the result of ground calibration and are very unlikely to be updated during the flight. The general processing scans a number of times over the input files to generate the required output data so no large internal buffers are required. The only processing size limitation is that at maximum 250 input files can be specified and sorted because an array of indices is used that is stored in a string and a configurable maximum is set to 20 time frames (each with their linear correlation between hack and SCUT). In addition that program can take a pointing target file as input that specifies time periods (SCUT) during which the instrument was pointing at certain targets or when the instrument was performing certain activities (ie. TVac testsing). If this file is not specified or if the file time doesn't match any of the periods in the target file (not even one of the default frames) then the pointing keyword in the FITS file will indicate unknown. The program produces additional output files that are to be used for the trending and engineering assessment of the LAMP instrument. The main file in this category is the LAMP HK engineering file that contains a single line for each received LAMP HK packet and lists all parameters for this HK packet. Similar files are produced for the LRO HK files that contain LAMP related parameters, these are also to be used for trending and engineering assessment and the data from these files is not included in the FITS file at all. Memory dump packets encountered in the telemetry stream will result in the generation of binary dump files that contain the concatenated data of the memory dump requests. For the three special memory dump areas in the RAM memory, error log, parameter file and door life test results, also corresponding text files will be generated that contain an annotated version of these engineering data. The parameter dump files may also be generated by LAMP-LIMA whenever an initial complete parameter set is captured or when a reported parameter changes value. The calling format for the program is: lamp-lima [-v[#]] - verbose [-d] - data directory [-e] - engineering directory [-t] - target file [-c] - calibration tables [-h] - disable hack correction [-p] - parameter decommutation [-l] - log file generation The option -c generates a full engineering calibration table for all LAMP conversions on standard output for all possible 256 input values (all LAMP engineering calibration raw values are a single byte). The option -h enables the correction of time hack entries in the science data, this is corrected both for the LIMA processing and for the generated FITS file, so successive steps don't need to take this into account any more, the FITS keyword HACKCORR indicates the number of corrections made in the whole FITS file. Note that this option does not affect the processing of the hacktime information in the housekeeping file, so the hack clock versus SCUT correlation processing is unaffected. It may affect though the processing of the science file(s) where the duration of the pixellist acquisitions is determined based on the hack times found in the science file. The option -p enables the extended parameter decommutation and reporting, LIMA will generate a parameter dump whenever the complete set is received (nominally 71 seconds after power up) and whenever a changed parameter is detected. This requires that the parameters are reported, if a single parameter is selected for reporting before the cyclic reporting is completed no (or incomplete) parameter dumps will be generated. The option -v# specifies the selected verbose level, from 0 - terse to a current maximum of 7. With the lowest (default) setting hardly any progress is reported as the program will nominally be run unsupervised, in that case only major error messages are printed. All additional output of the program is preceded by a '#' character so it can easily be distinguished from the error messages. This additional output may be useful for debugging and problem investigation purposes. The option -s activates the spacecraft telemetry processing function, it will decode three LAMP related S/C HK packets into engineering text files, currently it handles the ApId's 32, 100 and 106. A manual selection of this mode was required since the LRO ICD was changed to exclude the standard header form these produced telemetry extracts. Options -d and -e allow for the specification of the directories in which the result files are to be stored, if no destination directories are specified then the resulting files will be generated in the current directory. The option -t specifies the target file (may be complete path to the target file) that specifies the target (or observation activity) of the LAMP instrument for a specified time interval. The option -l activates a logging system that generates a logging file (with a base name identical to the engineering file, as long as this name does not yet exists) that provides an attempt of an intelligent log of the LAMP reported activities. It is limited version of the logging system that is provided by the GSE when processing the real-time data stream. ------------------------------------------------------------------------------- MODIFICATION HISTORY $Author: mversteeg $ $State: Exp $ ------------------------------------------------------------------------------- $Log: Acquire.c,v $ -----------------------------------------------------------------------------*/ /* includes -----------------------------------------------------------------*/ #include #include #include #include #include // Straight include mechanism is used to tie all four source files into a // single compilation unit, no separate compilation is required since // overall compilation can be performed quickly. #include "lamp-lima.h" // general definitions #include "lal_cali.h" // calibration curve calculations #include "lal_util.h" // general utility functions #include "lal_fits.h" // all FITS file generation and science TM decoding /* defines ------------------------------------------------------------------*/ /* typedefs -----------------------------------------------------------------*/ /* globals ------------------------------------------------------------------*/ /* locals -------------------------------------------------------------------*/ /* forward declarations -----------------------------------------------------*/ /*-----------------------------------------------------------------------*/ void ShowCalibrationTables(char curtime[]) /* ARGS current time string RETURNS none DESCRIPTION Print a complete set of calibration tables that show the converted values each of the LAMP engineering calibration functions used in LIMA. As each of the calibrated values consists of a single byte the table will be 256 entries long. */ { int i; printf("Calibration tables, status %s\n", curtime); printf(" %s - %s\r\n",CALFILENAME,CALFILEDATE); printf("Cnt HvSet McpV AnoV StrpI DscV Temp \n"); for (i=0;i<256;i++) { printf("%3d %7.3f %7.3f %4.0f %5.2f %4.2f %7.1f \n",i , HvSetCal(i), McpVCal(i), AnoVCal(i) , StpICal(i), DiscrCal(i), TempsCal(i)); } } /* ShowCalibrationTables() */ /*-----------------------------------------------------------------------*/ void saveErrorLogDump(SUTC stim,char edir[],BYTE errlog[]) /* ARGS log file base name, time SUTC, engineering data directory name (output), error log (array of 128 bytes) RETURNS none DESCRIPTION Write an ASCII description of a dumped error log contents to the specified file. The contents of the error log consists of 8 logged entries each specifying a time, the error and the reported status of commands accepted, commands failed and safety at the time of the error. */ { FILE *dfp; unsigned i; char filename[MAXFILENAME]; LONG logtime; makeMemDataFileName(stim.sec,ERRLOGNAME,edir,filename); if ((dfp=fopen(filename,FMODEWRITE))!=NULL) { printf("# create error log file: %s\n",filename); fprintf(dfp,"# LAMP -- Error Data Dump Log --\n"); fprintf(dfp,"# dumped: %14.3f %s\n",realSUTC(stim),utcTimeStr(stim)); fprintf(dfp,"# %s\n",VERSIONSTR); fprintf(dfp,"#--------------------------------------------------\n"); fprintf(dfp,"MET (sec)\terror\ttc-acc\ttc-fail\tsafety\n"); for (i=0;i<8;i++) { logtime= (errlog[i*8 ]<<24)+(errlog[i*8+1]<<16) +(errlog[i*8+2]<< 8)+(errlog[i*8+3] ); fprintf(dfp,"%9ld\t0x%02x\t0x%02x\t0x%02x\t0x%02x\n" ,logtime,errlog[i*8+4],errlog[i*8+5] ,errlog[i*8+6],errlog[i*8+7]); } fprintf(dfp,"#--------------------------------------------------\n"); fclose(dfp); } else { printf("ERROR: failed to create error log file: %s\n",filename); } } /* saveErrorLogDump() */ /*-----------------------------------------------------------------------*/ void saveWorkParamDump(SUTC stim,char edir[],BYTE parms[],char mode[]) /* ARGS log file base name, time SUTC, engineering data directory name (output), work param dump (array of 128 bytes) RETURNS none DESCRIPTION Write an ASCII description of a dumped parameter list to the specified file. The contents of the parameter file dump consists of 128 bytes, but only the 71 that have a defined meaning will be printed. For each parameter file entry the number and name is printed followed by the hexadecimal value, in addition where applicable either the value in decimal, an engineering value or a decoded value will be printed. */ { FILE *dfp; unsigned i; char filename[MAXFILENAME]; char hwstr[16][3] = {"?0","L1","L2","FM","L3","SM","?6","HD" ,"?8","TS","?A","?B","?C","?D","?E","?F"}; char modestr[16][7]={"off ","-(A+B)","A·(-B)","-B " ,"(-A)·B","-A ","AxB ","-(A·B)" ,"A·B ","-(AxB)","A ","A+(-B)" ,"B ","(-A)+B","A+B ","on "}; makeMemDataFileName(stim.sec,PARAMSNAME,edir,filename); if ((dfp=fopen(filename,FMODEWRITE))!=NULL) { printf("# create prameter dump file: %s\n",filename); fprintf(dfp,"# LAMP - Parameter Dump Log (%s) -\n",mode); fprintf(dfp,"# dumped: %14.3f %s\n",realSUTC(stim),utcTimeStr(stim)); fprintf(dfp,"# %s\n",VERSIONSTR); fprintf(dfp,"#--------------------------------------------------\n"); fprintf(dfp," #\tentry \t hex\tengineering\n"); fprintf(dfp," 0\tgeneral \t 0x%02x\twpa %1d, adsaf %1d, adlts %1d, hvlts %1d\n" ,parms[ 0],(parms[ 0]>>2)&1,(parms[ 0]>>4)&1 ,(parms[ 0]>>5)&1,(parms[ 0]>>6)&1); fprintf(dfp," 1\theaters \t 0x%02x\tmirrh %1d, mirrs %1d, grath %1d, grats %1d\n" ,parms[ 1],(parms[ 1]>>0)&3,(parms[ 1]>>2)&1 ,(parms[ 1]>>4)&3,(parms[ 1]>>6)&1); fprintf(dfp," 2\tcmdtimeout\t 0x%02x\t%d sec\n",parms[ 2],parms[ 2]); fprintf(dfp," 3\tcmaxerror \t 0x%02x\t%d\n",parms[ 3],parms[ 3]); fprintf(dfp," 4\twpatimeout\t 0x%02x\t%d sec\n",parms[ 4],10*parms[ 4]); fprintf(dfp," 5\tsmacontrol\t 0x%02x\t%d ms\n",parms[ 5],5*parms[ 5]); fprintf(dfp," 6\tadcontrol \t 0x%02x\t%4.1f sec\n",parms[ 6],parms[ 6]/10.0); if (parms[ 7]==0xff) { fprintf(dfp," 7\treportparm\t 0x%02x\tcycle\n",parms[ 7],parms[ 7]); } else { fprintf(dfp," 7\treportparm\t 0x%02x\t%d\n",parms[ 7],parms[ 7]); } fprintf(dfp," 8\treportsubs\t 0x%02x\t%d\n",parms[ 8],parms[ 8]); fprintf(dfp," 9\thwversion \t 0x%02x\t%s\n",parms[ 9],hwstr[parms[ 9]&0x0f]); fprintf(dfp,"10\tstimenable\t 0x%02x\t%d\n",parms[10],parms[10]); fprintf(dfp,"11\thvenable \t 0x%02x\thv1 %d, hv2 %d\n",parms[11],(parms[11]>>1)&1,parms[11]&1); fprintf(dfp,"12\tdiscrim \t 0x%02x\t%d =%5.2f Volt\n",parms[12],parms[12],DiscrCal(parms[12])); fprintf(dfp,"13\thvlevel \t 0x%02x\t%d =%7.3f kVolt\n",parms[13],parms[13],HvSetCal(parms[13])); fprintf(dfp,"14\thvstepfrac\t 0x%02x\t16/%d =%6.2f\n",parms[14],parms[14],16.0/parms[14]); fprintf(dfp,"15\thvsteptime\t 0x%02x\t%d sec\n",parms[15],parms[15]); fprintf(dfp,"16\thvsafelevl\t 0x%02x\t%d =%7.3f kVolt\n",parms[16],parms[16],HvSetCal(parms[16])); fprintf(dfp,"17\tpixlsthack\t 0x%02x\t%d ms\n",parms[17],4<<(parms[17]&7)); fprintf(dfp,"18\thistoexp \t0x%04x\t%d sec\n",(parms[18]<<8)+parms[19] ,(parms[18]<<8)+parms[19]); fprintf(dfp,"20\tacqtimeout\t0x%04x\t%d sec\n",(parms[20]<<8)+parms[21] ,(parms[20]<<8)+parms[21]); for (i=22;i<30;i++) { fprintf(dfp,"%2d\thotseg%d\t 0x%02x\t(%2d,%4d) - (%2d,%4d)\n",i,i-21,parms[i] ,4*((parms[i]>>5)&0x7) ,32*(parms[i]&0x1f) ,4*((parms[i]>>5)&0x7)+3,32*(parms[i]&0x1f)+31); } fprintf(dfp,"30\tltsaoffset\t0x%04x\t%d\n",(parms[30]<<8)+parms[31] ,(parms[30]<<8)+parms[31]); fprintf(dfp,"30\tltsboffset\t0x%04x\t%d\n",(parms[32]<<8)+parms[33] ,(parms[32]<<8)+parms[33]); fprintf(dfp,"34\tltsagain \t 0x%02x\t/%d = %5.3f\n",parms[34],1<<(parms[34]&0x0f),1.0/(1<<(parms[34]&0x0f))); fprintf(dfp,"35\tltsbgain \t 0x%02x\t/%d = %5.3f\n",parms[35],1<<(parms[35]&0x0f),1.0/(1<<(parms[35]&0x0f))); fprintf(dfp,"36\tltsalolimt\t 0x%02x\t%d\n",parms[36],parms[36]); fprintf(dfp,"37\tltsblolimt\t 0x%02x\t%d\n",parms[37],parms[37]); fprintf(dfp,"38\tltsahilimt\t 0x%02x\t%d\n",parms[38],parms[38]); fprintf(dfp,"39\tltsbhilimt\t 0x%02x\t%d\n",parms[39],parms[39]); fprintf(dfp,"40\tltsmode \t 0x%02x\tlight %s, dark %s\n",parms[40] ,modestr[(parms[40]>>4)&0x0f],modestr[(parms[40])&0x0f]); fprintf(dfp,"41\tltsdelayon\t 0x%02x\t%4.1f sec\n",parms[41],parms[41]/10.0); fprintf(dfp,"42\tltsdelayof\t 0x%02x\t%4.1f sec\n",parms[42],parms[42]/10.0); fprintf(dfp,"43\tltsmaxcycl\t 0x%02x\t%d\n",parms[43],parms[43]); fprintf(dfp,"44\tmaxcountr \t0x%04x\t%d Hz\n",(parms[44]<<8)+parms[45],(parms[44]<<8)+parms[45]); fprintf(dfp,"46\thvlowsafty\t 0x%02x\t%d =%7.3f kVolt\n",parms[46],parms[46],HvSetCal(parms[46])); fprintf(dfp,"47\tdacadcfact\t 0x%02x\t%d/240 =%5.2f\n",parms[47],parms[47],parms[47]/240.0); fprintf(dfp,"48\thvmaxhvset\t 0x%02x\t%d =%7.3f kVolt\n",parms[48],parms[48],HvSetCal(parms[48])); fprintf(dfp,"49\thvmcptol \t 0x%02x\t%4.1f Volt\n",parms[49],25.0*parms[49]); fprintf(dfp,"50\thvfailmcp \t 0x%02x\t%d *\n",parms[50],parms[50]); fprintf(dfp,"51\thvmaxstrpi\t 0x%02x\t%d =%6.2f uA\n",parms[51],parms[51],SumICal(parms[51])); fprintf(dfp,"52\thvfailstrp\t 0x%02x\t%d *\n",parms[52],parms[52]); fprintf(dfp,"53\thvminanodv\t 0x%02x\t%d =%5.0f Volt\n",parms[53],parms[53],AnoVCal(parms[53])); fprintf(dfp,"54\thvmaxanodv\t 0x%02x\t%d =%5.0f Volt\n",parms[54],parms[54],AnoVCal(parms[54])); fprintf(dfp,"55\thvfailanod\t 0x%02x\t%d *\n",parms[55],parms[55]); fprintf(dfp,"56\tmaxmir1tmp\t 0x%02x\t%d =%5.1f degC\n",parms[56],parms[56],TempsCal(parms[56])); fprintf(dfp,"57\tmaxmir2tmp\t 0x%02x\t%d =%5.1f degC\n",parms[57],parms[57],TempsCal(parms[57])); fprintf(dfp,"58\tmaxgrt1tmp\t 0x%02x\t%d =%5.1f degC\n",parms[58],parms[58],TempsCal(parms[58])); fprintf(dfp,"59\tmaxgrt2tmp\t 0x%02x\t%d =%5.1f degC\n",parms[59],parms[59],TempsCal(parms[59])); fprintf(dfp,"60\tmaxcdhetmp\t 0x%02x\t%d =%5.1f degC\n",parms[60],parms[60],TempsCal(parms[60])); fprintf(dfp,"61\tmaxdethtmp\t 0x%02x\t%d =%5.1f degC\n",parms[61],parms[61],TempsCal(parms[61])); fprintf(dfp,"62\ttempmask \t 0x%02x\t%d\n",parms[62],parms[62]); fprintf(dfp,"63\tsafemask \t 0x%02x\t%d\n",parms[63],parms[63]); fprintf(dfp,"64\tsafetimeot\t0x%04x\t%d sec\n",(parms[64]<<8)+parms[65],(parms[64]<<8)+parms[65]); fprintf(dfp,"66\tdebugtest \t 0x%02x\t%d\n",parms[66],parms[66]); fprintf(dfp,"69\twritecycls\t0x%04x\t%d\n",(parms[69]<<8)+parms[70],(parms[69]<<8)+parms[70]); fprintf(dfp,"#--------------------------------------------------\n"); fclose(dfp); } else { printf("ERROR: failed to create prameter dump file: %s\n",filename); } } /* saveWorkParamDump() */ /*-----------------------------------------------------------------------*/ void saveApDoorLogDump(SUTC stim,char edir[],BYTE adlog[]) /* ARGS log file base name, time SUTC, engineering data directory name (output), door life log (array of 128 bytes) RETURNS none DESCRIPTION Write an ASCII description of a dumped door life log to the specified file. The contents of the error log consists of up to 16 logged entries each describing a door test cycle. For each cycle the open and close start and stop times will be printed in milliseconds */ { FILE *dfp; unsigned i; char filename[MAXFILENAME]; makeMemDataFileName(stim.sec,ADLIFENAME,edir,filename); if ((dfp=fopen(filename,FMODEWRITE))!=NULL) { printf("# create aperture door life dump file: %s\n",filename); fprintf(dfp,"# LAMP -- Aperture Door Life Log --\n"); fprintf(dfp,"# dumped: %14.3f %s\n",realSUTC(stim),utcTimeStr(stim)); fprintf(dfp,"# %s\n",VERSIONSTR); fprintf(dfp,"#--------------------------------------------------\n"); fprintf(dfp,"open strt\topen end\tclose strt\tclose end\n"); fprintf(dfp,"(ms) \t(ms) \t(ms) \t(ms) \n"); for (i=0;i<16;i++) { fprintf(dfp,"%9.1f\t%9.1f\t%9.1f\t%9.1f\n" ,((adlog[i*8 ]<<8)+adlog[i*8+1])/10.0 ,((adlog[i*8+2]<<8)+adlog[i*8+3])/10.0 ,((adlog[i*8+4]<<8)+adlog[i*8+5])/10.0 ,((adlog[i*8+6]<<8)+adlog[i*8+7])/10.0); } fprintf(dfp,"#--------------------------------------------------\n"); fclose(dfp); } else { printf("ERROR: failed to create aperture door life dump file: %s\n",filename); } } /* saveApDoorLogDump() */ /*-----------------------------------------------------------------------*/ int findAcquisitions(char verbose, unsigned char sfil[], FILEHEADER file[] , ACQLIST *acq) /* ARGS verbose status, science files index, list of all file headers, acquisition list, RETURNS 1 - processing completed successfully DESCRIPTION This is the first scan over all the acquisitions in the science file(s), it wil populate the list of acquistions with the basic information from the science frames, consisting only of the generation time, the science frame header and the acquisition type (directly derived form the frame ident word). Making this list ensures that the acquisition list entries match the frames in the list of raw frames. */ { unsigned si; int i; FILE *fp; FILEHEADER hdr; char ok = 1; SUTC stim; WORD ident; double tim; LONG checksum; WORD w; acq->acquisitions= 0; acq->hisacq= 0; acq->pixacq= 0; printf("# generate a list of all frames in the %d science file(s)\n",strlen(sfil)); for (si=0;si1) { printf("## Sci %d %d %s (mode %s) ##\n",si,sfil[si],file[sfil[si]].realname,FMODEREAD); } if ((fp = fopen(file[sfil[si]].realname,FMODEREAD))!=NULL) { if (readFileHeader(fp,&hdr)) { readLONG(fp,&(stim.sec)); // pre-read so file status will show eof() while ((!feof(fp))&&(ok)) { readWORD(fp,&(stim.sub)); tim= realSUTC(stim); readDROW(fp,&ident); checksum= CHECKSUMSEED; for (i=1;i<32768;i++) { readDROW(fp,&w); checksum= updateChecksum(checksum,w); } if (feof(fp)) { ok= 0; printf("ERROR: file ended too early %s\n",file[sfil[si]].realname); } else { if (verbose>2) { printf("### frame 0x%04x time %s (%14.3f)\n",ident,utcTimeStr(stim),realSUTC(stim)); } if (acq->acquisitionsac[acq->acquisitions].gentime= tim; acq->ac[acq->acquisitions].header= ident; acq->ac[acq->acquisitions].checksum= checksum; acq->ac[acq->acquisitions].scihdr= NOHKHEADER; acq->ac[acq->acquisitions].start= tim-1.5; // silly guess acq->ac[acq->acquisitions].stop= tim-0.5; // best guess for the time being acq->ac[acq->acquisitions].hackstart= -1; acq->ac[acq->acquisitions].hackstop= -1; acq->acquisitions++; if (((ident>>15)&1)==PIXELLIST) { acq->pixacq++; } else { acq->hisacq++; } } else { ok= 0; printf("ERROR: too many science frames\n",acq->acquisitions+1); } readLONG(fp,&(stim.sec)); // pre-read so file status will show eof() } } } else { printf("ERROR: file too small (<64): %s\n",file[sfil[i]].realname); ok= 0; } fclose(fp); } else { printf("ERROR: cannot access input file: %s\n",file[sfil[i]].realname); ok= 0; } } if (acq->acquisitions==0) { printf("# science collection completed: no frames found\n"); } else { for(i=1;i<(int)acq->acquisitions;i++) { if ((acq->ac[i].header&0x0fff)!=((acq->ac[i-1].header+1)&0x0fff)) { printf("WARNING: science frame numbers not consecutive %d follows %d (at %d)\n" ,acq->ac[i].header&0x0fff,acq->ac[i-1].header&0x0fff,i); } } printf("# science collection completed: %d frames found (%d-%d)\n" ,acq->acquisitions,acq->ac[0].header&0x0fff ,acq->ac[acq->acquisitions-1].header&0x0fff); } return(ok); } /* findAcquisitions() */ /*-----------------------------------------------------------------------*/ int matchAcquisitions(char verbose, unsigned char hfil[], FILEHEADER file[] , HKOVERVIEW *hovr, ACQLIST *acq, int sciencefiles) /* ARGS verbose status, housekeeping files index, list of all file headers, engineering data directory name (output), hk overview structure (output), acquisition list (output) RETURNS 1 if processing completed succesfully DESCRIPTION This function performs the first scan of all the sorted housekeeping files and builds up the housekeeping overview and adds information to the acquisition list based on the housekeeping data. The housekeeping overview consits of a number of parameters that describe the instrument status as reported in the housekeeping data over the whole period covered by the input file(s). It includes calculating minima, maxima and average values for certain parameters, determining the whether state variables changed during the covered period or where constant and finding whether the complete dataset covers a continuous period. Included in the housekeeping overview is also the clock-hack correlation function. Any memory dump packets in the housekeeping data files are simply skipped, these will be processed in another scan. Starts of acquisitions are detected in the housekeeping stream by a change in the processor operational state to histogram or pixellist acquisition for the start of a new acquisition or by a change in the reported frame number when an acquisition continues into a new frame. An end of an acquisition is detected either by the instrument reporting leaving the acquisition state or by the detection of the start of a new acquisition. For each acquisition the start and stop times are recorded and during an acquisition the aperture door state, LTS state, STIM state and HVPS states are all tracked for changes and the minimum and maximum countrates are determined. The clock correlation function calculates for each continuous time series a linear least squares fit time correlation. The function supports generating up to 20 separate sets of time correlation for cases where the time sequence is discontinuous due to power or reset cycles. If the The verbose parameter controls the amount of status output that will be generated on standard output. For a verbose level of 2 or above the extracted acquisition information (list) is also output to standard output, for an verbose level of 3 or above also sends list of timeframes to standard output. */ { unsigned fi; int i; FILE *hkfp; FILEHEADER hdr; char ok = 1; LONG time= -1; CCSDSHEADER ccsds; SUTC stim; BYTE hkpkt[LAMPHKPKTLEN-5]; BYTE ostat; BYTE lastostat= 2; WORD frameno= 0xffff; WORD lastframeno= 0xffff; WORD acqtimout= 0; WORD lastacqtimout= 0; WORD scihdr; int expect; BYTE ltsdark; BYTE firstsync= 1; int hacktime= 0; int lasthacktime= -1; SUTC laststim= {-1,0}; double dstim,dhack; ACQUIT curacq; int acqi= 0; int match= 0; int found; initializeHkOverview(hovr); // initialize the overview structure resetHackWrap(); // initialize hack clock handling if (strlen(hfil)>0) { printf("# scan %d HK file(s) and establish acquisition links\n",strlen(hfil)); if (!sciencefiles) { printf("WARNING: no science files to match to, scanning only\n"); } for (fi=0;fi2) { printf("## Hk %d, index %d: %s\n",fi,hfil[fi],file[hfil[fi]].realname); } if ((hkfp = fopen(file[hfil[fi]].realname,FMODEREAD))!=NULL) { if (readFileHeader(hkfp,&hdr)) { if ((time!=-1)&&(time+1!=hdr.Start.sec)) { printf("WARNING: file boundary packet time discontinuity %d after %d\n" ,hdr.Start.sec,time); hovr->contin= 0; } while ((!feof(hkfp))&&(ok)) { if (readCcsdsHeader(hkfp,&ccsds)) { if (ccsds.apid==LAMPHKAPID) { if (ccsds.pktlen!=LAMPHKPKTLEN) { printf("ERROR: unexpected HK packet size %d\n",ccsds.pktlen); ok= 0; } readLONG(hkfp,&(stim.sec)); readWORD(hkfp,&(stim.sub)); readString(hkfp,hkpkt,ccsds.pktlen-5); // sec & sub already read updateHkOverview(ccsds, stim, hkpkt, hovr); hacktime= unwrappedHackTime(hkpkt[24],hkpkt[25]); if ( (hovr->htf[hovr->timeframes].firsthack<0) ||(hacktimehtf[hovr->timeframes].firsthack) ) { hovr->htf[hovr->timeframes].firsthack= hacktime; } // check for discontinous clock-hack relation: restart of time change dstim= realSUTC(stim)-realSUTC(laststim); dhack= (hacktime-lasthacktime)*NOMINALHACKCLOCK; if ( (lasthacktime>=0)&&(realSUTC(laststim)>SYNCHEDTIME) &&(flabs(dstim-dhack)>MAXCLOCKMISMATCH) ) { if (realSUTC(stim)timeframeshtf[hovr->timeframes++])); } else { printf("ERROR: too many time frames\n"); } } updateClockStatistics(hacktime, realSUTC(stim)); // find start and stop of acquisitions ostat= (hkpkt[0]&0x70)>>4; scihdr= (hkpkt[14]*256)+hkpkt[15]; frameno= scihdr&0x0fff; acqtimout= (hkpkt[34]*256)+hkpkt[35]; ltsdark= (hkpkt[16]&0x04)>>2; // if ( ((ostat==3)||(ostat==7)) // &&(lastostat!=3)&&(lastostat!=7) ) if (acqtimout>lastacqtimout) { // real start of start acquisition (including HV rampup), curacq.start= realSUTC(stim); curacq.apdstate= 1+((hkpkt[16]&0x30)>>4); curacq.ltsstate= 1+ltsdark; curacq.stimstate= 1+(hkpkt[17]&0x01); curacq.hvpsstate= 1+(((hkpkt[1]&0x30)>>4)&((hkpkt[1]&0xc0)>>6)); curacq.hackstart= hacktime; curacq.mincr= (hkpkt[18]*256+hkpkt[19]); curacq.maxcr= (hkpkt[18]*256+hkpkt[19]); if (verbose>2) { printf("### start acquisition: %14.3f, %d sec\n" ,curacq.start,acqtimout); } } if ((frameno!=lastframeno)&&(lastframeno!=0xffff)) { // frame send if (hkpkt[14]&0x20) // last block { curacq.stop= realSUTC(stim)-1.0; curacq.hackstop= lasthacktime; } else { curacq.stop= realSUTC(stim); curacq.hackstop= hacktime; } if (verbose>2) { printf("### frame send: %14.3f, new %x, last %x\n" ,curacq.stop,frameno,lastframeno); } curacq.frameno= frameno; curacq.scihdr= scihdr; if (((curacq.scihdr>>15)&1)==PIXELLIST) { curacq.hackrate= 1+((hkpkt[17]&0x70)>>4); } else /* histogram */ { curacq.hackrate= 0; } // Now update as many acquisition entries as match this update expect= (frameno-lastframeno)&0xfff; if ((expect==2)&&(verbose)&&(sciencefiles)) { printf("# NOTE two frames assumed within 1 second (0x%03x-0x%03x)\n" ,(lastframeno+1)&0xfff,frameno); } else if (expect==3) { printf("WARNING: three frames assumed within 1 second (0x%03x-0x%03x)\n" ,(lastframeno+1)&0xfff,frameno); } else if (expect>3) { printf("WARNING: to many frames assumed within 1 second (0x%03x-0x%03x)\n" ,(lastframeno+1)&0xfff,frameno); expect= 0; } found= 0; while (lastframeno!=frameno) { lastframeno= (lastframeno+1)&0xfff; for (acqi=0;acqiacquisitions;acqi++) { if ( ((curacq.stop-1.0)ac[acqi].gentime) &&(acq->ac[acqi].gentime<(curacq.stop+HSDATATIMELIMIT)) &&(((acq->ac[acqi].header)&0xfff)==lastframeno) ) { found++; if (verbose>2) { printf("## frame match: acq %d, 0x%04x 0x%04x %14.3f\n" ,acqi,curacq.scihdr,acq->ac[acqi].header,curacq.stop); } match++; acq->ac[acqi].start= curacq.start; acq->ac[acqi].stop= curacq.stop; acq->ac[acqi].scihdr= curacq.scihdr; acq->ac[acqi].frameno= curacq.frameno; acq->ac[acqi].apdstate= curacq.apdstate; acq->ac[acqi].ltsstate= curacq.ltsstate; acq->ac[acqi].stimstate= curacq.stimstate; acq->ac[acqi].hvpsstate= curacq.hvpsstate; acq->ac[acqi].hackrate= curacq.hackrate; acq->ac[acqi].mincr= curacq.mincr; acq->ac[acqi].maxcr= curacq.maxcr; acq->ac[acqi].hackstart= curacq.hackstart; acq->ac[acqi].hackstop= curacq.hackstop; } else if ( (verbose>2) &&((curacq.stop-1.0)ac[acqi].gentime) &&(acq->ac[acqi].gentime<(curacq.stop+HSDATATIMELIMIT)) ) { printf("WARNING: frame inside time window (%d 0x%03x 0x%04x %14.3f %14.3f)\n" ,acqi,lastframeno,acq->ac[acqi].header ,curacq.stop,acq->ac[acqi].gentime); } else if ( (verbose>2) &&(lastframeno==(acq->ac[acqi].header&0xfff)) ) { printf("WARNING: frame number match (acq %d 0x%04x)\n" ,acqi,curacq.scihdr); } } } if ((expect!=found)&&(sciencefiles)) { printf("WARNING: no science frame found (%d) for frame #%d from HK\n" ,expect-found,curacq.frameno); } // possible start next acquisition (continued acquisition) curacq.start= realSUTC(stim); curacq.apdstate= 1+((hkpkt[16]&0x30)>>4); curacq.ltsstate= 1+ltsdark; curacq.stimstate= 1+(hkpkt[17]&0x01); curacq.hvpsstate= 1+(((hkpkt[1]&0x30)>>4)&((hkpkt[1]&0xc0)>>6)); curacq.hackstart= hacktime; curacq.mincr= (hkpkt[18]*256+hkpkt[19]); curacq.maxcr= (hkpkt[18]*256+hkpkt[19]); } else { statechange(curacq.apdstate,1+((hkpkt[16]&0x30)>>4)); statechange(curacq.ltsstate,1+ltsdark); statechange(curacq.stimstate,1+(hkpkt[17]&0x01)); statechange(curacq.hvpsstate,1+(((hkpkt[1]&0x30)>>4)&((hkpkt[1]&0xc0)>>6))); minmax((hkpkt[18]*256+hkpkt[19]),curacq.mincr,curacq.maxcr); } lasthacktime= hacktime; laststim= stim; lastframeno= frameno; lastostat= ostat; lastacqtimout= acqtimout; } else if (ccsds.apid=LAMPMDAPID) { // skip for now, process later when generating engineering output // printf("OK: Memory Dump packet ApId %d, len %d\n",ccsds.apid,ccsds.pktlen); skipBYTEs(hkfp,ccsds.pktlen+1); } else { printf("WARNING: skipping unexpected packet ApId %d, len %d\n" ,ccsds.apid,ccsds.pktlen); skipBYTEs(hkfp,ccsds.pktlen+1); } } else if (!feof(hkfp)) { printf("ERROR: incorrect CCSDS header near %d in file %s\n" ,ftell(hkfp),file[hfil[fi]].realname); ok= 0; } } } else { printf("ERROR: file too small (<64): %s\n",file[hfil[fi]].realname); ok= 0; } fclose(hkfp); } else { printf("ERROR: cannot access input file: %s\n",file[hfil[fi]].realname); ok= 0; } } calculateClockLeastSquaresFit(&(hovr->htf[hovr->timeframes++])); if (hovr->timeframes>1) { printf("WARNING: Multiple hack time frames found (%d)\n",hovr->timeframes); } } else { printf("WARNING: no housekeeping files to match science acquisitions\n"); hovr->htf[0].hackoffset= -1.0; hovr->htf[0].hackrate= NOMINALHACKCLOCK/3; hovr->htf[0].hackcorr= 0.0; } // determine if acquisitions are contineous and if all were matched acq->contin= (acq->acquisitions>0); expect= 0; for (i=0;iacquisitions;i++) { if ((acq->ac[i].start-acq->ac[i-1].stop)contin= 0; } if (acq->ac[i].hackstart>=0) { expect++; } } if ((expect!=acq->acquisitions)||(match!=acq->acquisitions)) { printf("WARNING: Not all science acquisitions matched/accounted for with HK (%d, %d of %d)\n" ,match,expect,acq->acquisitions); } // fill sentinel of acquisition list with 'unknown' values memset(&(acq->ac[MAXACQUISITIONS]),0,sizeof(ACQUIT)); acq->ac[MAXACQUISITIONS].start= 0.0; acq->ac[MAXACQUISITIONS].stop= 0.0; acq->ac[MAXACQUISITIONS].checksum= NOSCIENCE; if (verbose) { printf("## hk acquisition scan complete %d acquisitions matched out of %d\n",match,acq->acquisitions); if (verbose>1) { for(i=0;iacquisitions;i++) { printf("acq%3d: %14.3f %14.3f %8.1f 0x%04x\n",i ,acq->ac[i].start,acq->ac[i].stop ,(acq->ac[i].stop)-(acq->ac[i].start) ,acq->ac[i].scihdr); } } if (verbose>2) { printf("## hk acquisition scan complete %d timeframes found\n",hovr->timeframes); for (i=0;itimeframes;i++) { printf("htf%d:%10.3f %8.6f %5.3f %6d %9d %10.3f %10.3f\n",i ,hovr->htf[i].hackoffset,hovr->htf[i].hackrate ,hovr->htf[i].hackcorr ,hovr->htf[i].firsthack,hovr->htf[i].lasthack ,reconstructedSUTC(hovr->htf[i].firsthack,hovr->htf) ,reconstructedSUTC(hovr->htf[i].lasthack ,hovr->htf)); } } } if ((hovr->contin)!=1) { printf("WARNING: Hk packet stream not contiguous\n",ccsds.apid,ccsds.pktlen+1); } return(ok); } /* matchAcquisitions() */ /*-----------------------------------------------------------------------*/ void writeToEngineering(FILE *fp, int verbose, char str[]) /* ARGS output file pointer, verbose status, string RETURNS none DESCRIPTION Write the specified string to the engineering output file, if the selected verbose level is above 3 the string is also written to standard output, if the verbose level equals 3 a single dot is written to standard output. */ { if (verbose>3) { printf("%s\n",str); } else if(verbose==3) { printf("."); } fprintf(fp,"%s\n",str); } /* writeToEngineering() */ /*-----------------------------------------------------------------------*/ void writeEngineeringHeader(FILE *efp, int verbose) /* ARGS file pointer, verbose status RETURNS none DESCRIPTION Write the LAMP engineering housekeeping file header to the specified ASCII file. The header of this file list the LIMA file type, the version of LAMP-LIMA that produced the file and the time at which it was generated. Then a list of all the fields that will be listed in the data section, the parameter number and the format is added. All these line that form the description of the data following are preceded by a '#' character so it can be easily distinguished from the actual data. */ { char str[80]; char tim[80]; writeToEngineering(efp,verbose,"#\tLIMA Engineering Data file"); sprintf(str,"#\tgenerated by: %s",VERSIONSTR); writeToEngineering(efp,verbose,str); sprintf(str,"#\tgenerated on: %s",currentUtcTimeStr(tim)); writeToEngineering(efp,verbose,str); writeToEngineering(efp,verbose,"#\t-------------------------------------------------"); writeToEngineering(efp,verbose,"#\t1\tSUTC\treconstructed SUTC (sec)\t%14.3f sec"); writeToEngineering(efp,verbose,"#\t2\tSUTCSTR\tSUTC (string)\t%s"); writeToEngineering(efp,verbose,"#\t3\tH081CNT\tCCSDS Packet Count\t%d"); writeToEngineering(efp,verbose,"#\t4\tH081TIME\tCCSDS packet Time\t%14.3f sec"); writeToEngineering(efp,verbose,"#\t5\tLAHKOPSTAT\tOperating State\t%s"); writeToEngineering(efp,verbose,"#\t6\tLAHKSAFETYACT\tSafety Active\t%s"); writeToEngineering(efp,verbose,"#\t7\tLAHKLSTSAFETYACT\tLast Safety Active\t%s"); writeToEngineering(efp,verbose,"#\t8\tLAHKPWR\tPower State\t%s"); writeToEngineering(efp,verbose,"#\t9\tLAHKHVPS\tHVPS State\t%s"); writeToEngineering(efp,verbose,"#\t10\tLAHKTURNOFFREQ\tTurn Off Request\t%s"); writeToEngineering(efp,verbose,"#\t11\tLAHKWPADRV\tWPA Driven\t%s"); writeToEngineering(efp,verbose,"#\t12\tLAHKWPASWTCH\tWPA Switch\t%s"); writeToEngineering(efp,verbose,"#\t13\tLAHKHVPSSAFE\tHVPS Safe Plug\t%s"); writeToEngineering(efp,verbose,"#\t14\tLAHKACTRSAFE\tActuator Safe Plug\t%s"); writeToEngineering(efp,verbose,"#\t15\tLAHKMIRRORHTR\tMirror Heater\t%s"); writeToEngineering(efp,verbose,"#\t16\tLAHKGRATINGHTR\tGrating Heater\t%s"); writeToEngineering(efp,verbose,"#\t17\tLAHKCMDRCVD\tCommand received\t%d"); writeToEngineering(efp,verbose,"#\t18\tLAHKSYNCMSGRCVD\tSync Message Received\t%d"); writeToEngineering(efp,verbose,"#\t19\tLAHKSYNCPLSRCVD\tSync Pulse received\t%d"); writeToEngineering(efp,verbose,"#\t20\tLAHKCRITCMDPEND\tCritical Command Pending\t%s"); writeToEngineering(efp,verbose,"#\t21\tLAHKMEMDMPALLOWED\tMemory Dump Allowed\t%s"); writeToEngineering(efp,verbose,"#\t22\tLAHKTCIFSTAT\tTC Interface Status\t%s"); writeToEngineering(efp,verbose,"#\t23\tLAHKCMDACPTCNT\tCommands Accepted\t%d"); writeToEngineering(efp,verbose,"#\t24\tLAHKCMDREJCNT\tCommands Rejected\t%d"); writeToEngineering(efp,verbose,"#\t25\tLAHKCMDEXECNT\tCommands Executed\t%d"); writeToEngineering(efp,verbose,"#\t26\tLAHKLASTCMDACPT\tLast Command Accepted\t%s"); writeToEngineering(efp,verbose,"#\t27\tLAHKLASTCMDFAIL\tLast Command Failed\t%s"); writeToEngineering(efp,verbose,"#\t28\tLAHKLASTFAILCODE\tLast Fail Code\t%d"); writeToEngineering(efp,verbose,"#\t29\tLAHKCRITCMDTIMEOUT\tCritical Command Timeout\t%d"); writeToEngineering(efp,verbose,"#\t30\tLAHKSPHPKTCONTENT\tScience Data Type\t%s"); writeToEngineering(efp,verbose,"#\t31\tLAHKSPHMEM\tScience Memory Type\t%s"); writeToEngineering(efp,verbose,"#\t32\tLAHKSPHLASTBLK\tScience Last Block\t%s"); writeToEngineering(efp,verbose,"#\t33\tLAHKSPHHWACQ\tScience Hardware Acquisition\t%s"); writeToEngineering(efp,verbose,"#\t34\tLAHKSPHBLKNUM\tScience Block Number\t%d"); writeToEngineering(efp,verbose,"#\t35\tLAHKDETDORPOS\tDetector Door Position\t%s"); writeToEngineering(efp,verbose,"#\t36\tLAHKAPDORPOS\tAperture Door Position\t%s"); writeToEngineering(efp,verbose,"#\t37\tLAHKLTSDARK\tLTS Dark Status\t%s"); writeToEngineering(efp,verbose,"#\t38\tLAHKHVPSCMDSTAT\tHVPS Command Status\t%s"); writeToEngineering(efp,verbose,"#\t39\tLAHKHACKRATE\tHackRate\t%d ms"); writeToEngineering(efp,verbose,"#\t40\tLAHKHSTMOVRFLW\tHigh Speed Overflow\t%s"); writeToEngineering(efp,verbose,"#\t41\tLAHKACQMEM\tAcquisition Memory\t%s"); writeToEngineering(efp,verbose,"#\t42\tLAHKPXLSTIMSTAT\tPixel STIM Status\t%s"); writeToEngineering(efp,verbose,"#\t43\tLAHKCNTRATE\tCountrate\t%d Hz"); writeToEngineering(efp,verbose,"#\t44\tLAHKHVPSSETPT\tHVPS Set Point\t%5.3f kVolt"); writeToEngineering(efp,verbose,"#\t45\tLAHKEVTCNT\tEvent Count\t%d"); writeToEngineering(efp,verbose,"#\t46\tLAHKTIMEHACKCNT\tTime Hack Count\t%d"); writeToEngineering(efp,verbose,"#\t47\tLAHKPXLLSTCNT\tPixellist Count\t%d"); writeToEngineering(efp,verbose,"#\t48\tLAHKEXPTIMEOUT\tExposure Timeout\t%d sec"); writeToEngineering(efp,verbose,"#\t49\tLAHKLASTACQDONETIME\tLast Acquisition Done\t%d sec"); writeToEngineering(efp,verbose,"#\t50\tLAHKACQTIMEOUT\tAcquisition Timeout\t%d sec"); writeToEngineering(efp,verbose,"#\t51\tLAHKMCP1V\tMcp1 Voltage\t%5.3f kVolt"); writeToEngineering(efp,verbose,"#\t52\tLAHKANODE1V\tAnode1 Voltage\t%3.0f Volt"); writeToEngineering(efp,verbose,"#\t53\tLAHKSTRIP1I\tStrip1 Current\t%4.2f uA"); writeToEngineering(efp,verbose,"#\t54\tLAHKMCP2V\tMcp2 Voltage\t%5.3f kVolt"); writeToEngineering(efp,verbose,"#\t55\tLAHKANODE2V\tAnode2 Voltage\t%3.0f Volt"); writeToEngineering(efp,verbose,"#\t56\tLAHKSTRIP2I\tStrip2 Current\t%4.2f uA"); writeToEngineering(efp,verbose,"#\t57\tLAHKMAXMCPV\tMax Mcp Voltage\t%5.3f kVolt"); writeToEngineering(efp,verbose,"#\t58\tLAHKMAXSTRIPI\tMax Strip Current\t%4.2f uA"); writeToEngineering(efp,verbose,"#\t59\tLAHKDISCV\tDiscriminator Voltage\t%4.2f Volt"); writeToEngineering(efp,verbose,"#\t60\tLAHKLTSALO\tLTS A Below Threshold\t%d"); writeToEngineering(efp,verbose,"#\t61\tLAHKLTSAHI\tLTS A Above Threshold\t%d"); writeToEngineering(efp,verbose,"#\t62\tLAHKLTSBLO\tLTS B Below Threshold\t%d"); writeToEngineering(efp,verbose,"#\t63\tLAHKLTSBHI\tLTS B Above Threshold\t%d"); writeToEngineering(efp,verbose,"#\t64\tLAHKLTSREQUEST\tLTS Current Request\t%s"); writeToEngineering(efp,verbose,"#\t65\tLAHKLTSDELAYED\tLTS Delayed Request\t%s"); writeToEngineering(efp,verbose,"#\t66\tLAHKLTSARAW\tLTS A Raw\t%d counts"); writeToEngineering(efp,verbose,"#\t67\tLAHKLTSBRAW\tLTS B Raw\t%d counts"); writeToEngineering(efp,verbose,"#\t68\tLAHKLTSSAFECYCLES\tLTS Safe Cycles\t%d"); writeToEngineering(efp,verbose,"#\t69\tLAHKMIRRORSETPNT\tMirror Heater Setpoint\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t70\tLAHKGRATINGSETPNT\tGrating Heater Setpoint\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t71\tLAHKMIRRORATMP\tMirror Temperature 1\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t72\tLAHKMIRRORBTMP\tMirror Temperature 2\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t73\tLAHKGRATINGATMP\tGrating Temperature 1\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t74\tLAHKGRATINGBTMP\tGrating Temperature 2\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t75\tLAHKCDHELECTMP\tC&DH temperature\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t76\tLAHKDETHOUSETMP\tDetector Housing Temperature\t%5.2f °C"); writeToEngineering(efp,verbose,"#\t77\tLAHKTMPSAFETY\tTemperature Safety\t%s"); writeToEngineering(efp,verbose,"#\t78\tLAHKCYCLESAFETY\tCycles Safety\t%s"); writeToEngineering(efp,verbose,"#\t79\tLAHKANODESAFETY\tAnode Voltage Safety\t%s"); writeToEngineering(efp,verbose,"#\t80\tLAHKSTRIPSAFETY\tStrip Current Safety\t%s"); writeToEngineering(efp,verbose,"#\t81\tLAHKHVSAFETY\tHigh Voltage Safety\t%s"); writeToEngineering(efp,verbose,"#\t82\tLAHKBRIGHTSAFETY\tBright Object Safety\t%s"); writeToEngineering(efp,verbose,"#\t83\tLAHKSAFETYTIMEOUT\tSafety Timeout\t%d"); writeToEngineering(efp,verbose,"#\t84\tLAHKSAFETYOVRD\tSafety Override\t%s"); writeToEngineering(efp,verbose,"#\t85\tLAHKTMPSAFEMASK\tTemperature Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t86\tLAHKCYCLESAFEMASK\tCycles Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t87\tLAHKANODESAFEMASK\tAnode Voltage Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t88\tLAHKSTRIPSAFEMASK\tStrip Current Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t89\tLAHKHVSAFEMASK\tHigh Voltage Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t90\tLAHKBRIGHTSAFEMASK\tBright Object Safety Mask\t%s"); writeToEngineering(efp,verbose,"#\t91\tLAHKCODESTAT\tCode Status\t%s"); writeToEngineering(efp,verbose,"#\t92\tLAHKHWVER\tHardware Version\t%s"); writeToEngineering(efp,verbose,"#\t93\tLAHKSWVER\tSoftware Version\t%4.2f "); writeToEngineering(efp,verbose,"#\t94\tLAHKRXINT1OFFSTAT\tReceive Interrupt Status 1\t%d"); writeToEngineering(efp,verbose,"#\t95\tLAHKRXINT2OFFSTAT\tReceive Interrupt Status 2\t%d"); writeToEngineering(efp,verbose,"#\t96\tLAHKSYNC1STAT\tTime Sync Pulse Status 1\t%d"); writeToEngineering(efp,verbose,"#\t97\tLAHKSYNC2STAT\tTime Sync Pulse Status 2\t%d"); writeToEngineering(efp,verbose,"#\t98\tLAHKFRMERR1\tReceive Frame Error 1\t%d"); writeToEngineering(efp,verbose,"#\t99\tLAHKFRMERR2\tReceive Frame Error 2\t%d"); writeToEngineering(efp,verbose,"#\t100\tLAHKOVRRUN1\tReceive Overrrun 1\t%d"); writeToEngineering(efp,verbose,"#\t101\tLAHKOVRRUN2\tReceive Overrrun 2\t%d"); writeToEngineering(efp,verbose,"#\t102\tLAHKMEMCKSM\tMemory Checksum\t0x%04x"); writeToEngineering(efp,verbose,"#\t103\tLAHKPROCIDLE\tProcessor Idle Time\t%d"); writeToEngineering(efp,verbose,"#\t104\tLAHKPROCSCHED\tProcessor Schedule\t%d"); writeToEngineering(efp,verbose,"#\t105\tLAHKTESTSTAT\tTest Status\t0x%02x"); writeToEngineering(efp,verbose,"#\t106\tLAHKMINSTACK\tMinimum Stack Size\t%d"); writeToEngineering(efp,verbose,"#\t107\tLAHKFIRSTDEL\tFirst Deleted Task\t%d"); writeToEngineering(efp,verbose,"#\t108\tLAHKSLOWTASKSTAT\tSlow Task Status\t%s"); writeToEngineering(efp,verbose,"#\t109\tLAHKEXPMAXSTAT\tWatchdog Above 15\t%d"); writeToEngineering(efp,verbose,"#\t110\tLAHKEXPCNT\tWatchDog Expiration Count\t%d"); writeToEngineering(efp,verbose,"#\t111\tLAHKPARAMINDEX\tParameter Index\t%d"); writeToEngineering(efp,verbose,"#\t112\tLAHKPARAMVAL\tParameter Value\t%d"); writeToEngineering(efp,verbose,"#\t113\tLAHKPKTCKSM\tPacket Checksum\t0x%04x"); writeToEngineering(efp,verbose,"#\t-------------------------------------------------"); } /* writeEngineeringHeader() */ /*-----------------------------------------------------------------------*/ char* generateEngineeringFileString(int hacktime,SUTC stim,WORD pktcnt ,HACKTIMEFRAME htf[], BYTE hkpkt[],char str1[]) /* ARGS packet hack time, packet SUTC, packet count (from CCSDS header), housekeeping packet (byte array), string space RETURNS string DESCRIPTION Generate a single line (string) for the LAMP LRO engineering housekeeping file, based on one housekeeping packet and write it to the ASCII engineering output file. Each line consists of 113 entries that correspond to the fields in the housekeeping telemetry packet. Where applicable the values are converted to engineering values or enumerated types are represented in strings. In some cases (for instance error messages) this decoding is not performed as it would have increased the data volume too much. */ { char str2[700]; // !! WARNING debugger will only show first 256 characters double rs; SUTC recstim; rs= reconstructedSUTC(hacktime,htf); recstim.sec= (int)rs; recstim.sub= (int)(65536.0*(rs-recstim.sec)); // 1 2 3 4 5 6 7 8 9 10 11 12 sprintf(str2,"%14.3f\t%s\t%d\t%14.3f\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" ,rs ,utcTimeStr(recstim) ,pktcnt ,stim.sec+stim.sub/65536.0 ,OpStatStr[(hkpkt[0]&0x70)>>4] ,SafetyStr[(hkpkt[0]&0x08)>>3] ,LastSafetyStr[(hkpkt[0]&0x07)] ,ABBothStr[(hkpkt[1]&0xc0)>>6] ,ABBothStr[((hkpkt[1]&0x30)>>4)&((hkpkt[1]&0xc0)>>6)] ,TurnOffStr[(hkpkt[1]&0x04)>>2] ,OffOnStr[(hkpkt[1]&0x02)>>1] ,ActiveStr[(hkpkt[1]&0x01)] ); // 13 14 15 16 17 18 19 20 21 22 sprintf(str1,"%s\t%s\t%s\t%s\t%s\t%d\t%d\t%d\t%s\t%s\t%s" ,str2 ,ABBothStr[(hkpkt[2]&0xc0)>>6] ,ABBothStr[(hkpkt[2]&0x30)>>4] ,ABBothStr[(hkpkt[2]&0x0c)>>2] ,ABBothStr[(hkpkt[2]&0x03) ] ,(hkpkt[3]&0x80)>>7 ,(hkpkt[3]&0x40)>>6 ,(hkpkt[3]&0x20)>>5 ,PendingStr[(hkpkt[3]&0x10)>>4] ,DumpStr[(hkpkt[3]&0x08)>>3] ,TcStatStr[hkpkt[3]&0x07] ); // 23 24 25 26 sprintf(str2,"%s\t%d\t%d\t%d\t%s" ,str1 ,hkpkt[4]*256+hkpkt[5] ,hkpkt[6]*256+hkpkt[7] ,hkpkt[8]*256+hkpkt[9] ,CommandStr(hkpkt[10])); // 27 28 29 sprintf(str1,"%s\t%s\t%d\t%d" ,str2 ,CommandStr(hkpkt[11]) ,hkpkt[12] ,hkpkt[13]); // 30 31 32 33 34 sprintf(str2,"%s\t%s\t%s\t%s\t%s\t%d" ,str1 ,AcqStr[(hkpkt[14]&0x80)>>7] ,ABStr[(hkpkt[14]&0x40)>>6] ,LastStr[(hkpkt[14]&0x20)>>5] ,HwCtrlStr[(hkpkt[14]&0x10)>>4] ,(hkpkt[14]*256+hkpkt[15])&0xfff); // 35 36 37 38 39 40 41 42 sprintf(str1,"%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s" ,str2 ,DetDoorStr[(hkpkt[16]&0xc0)>>6] ,ApDoorStr[(hkpkt[16]&0x30)>>4] ,LtsDarkStr[(hkpkt[16]&0x04)>>2] ,ABBothStr[(hkpkt[16]&0x03)] ,4<<((hkpkt[17]&0x70)>>4) ,OverflowStr[(hkpkt[17]&0x04)>>2] ,ABStr[(hkpkt[17]&0x02)>>1] ,StimStr[(hkpkt[17]&0x01)]); // STIM // 43 44 45 46 47 48 49 50 sprintf(str2,"%s\t%d\t%6.3f\t%d\t%d\t%d\t%d\t%d\t%d" ,str1 ,hkpkt[18]*256+hkpkt[19] ,HvSetCal(hkpkt[20]) ,hkpkt[21]*65536+hkpkt[22]*256+hkpkt[23] ,hkpkt[24]*256+hkpkt[25] ,hkpkt[26]*256+hkpkt[27] ,hkpkt[28]*256+hkpkt[29] ,hkpkt[30]*16777216+hkpkt[31]*65536 +hkpkt[32]*256+hkpkt[33] ,hkpkt[34]*256+hkpkt[35]); // 51 52 53 54 sprintf(str1,"%s\t%5.3f\t%3.0f\t%4.2f\t%5.3f" ,str2 ,McpVCal(hkpkt[36]) ,AnoVCal(hkpkt[37]) ,StpICal(hkpkt[38]) ,McpVCal(hkpkt[39])); // 55 56 57 58 59 sprintf(str2,"%s\t%3.0f\t%4.2f\t%5.3f\t%4.2f\t%4.2f" ,str1 ,AnoVCal(hkpkt[40]) ,StpICal(hkpkt[41]) ,McpVCal(hkpkt[42]) ,SumICal(hkpkt[43]) ,DiscrCal(hkpkt[44])); // 60 61 62 sprintf(str1,"%s\t%d\t%d\t%d\t%d\t%s\t%s\t%d\t%d\t%d" ,str2 ,(hkpkt[45]&0x80)>>7 ,(hkpkt[45]&0x40)>>6 ,(hkpkt[45]&0x20)>>5 ,(hkpkt[45]&0x10)>>4 ,ReqStr[(hkpkt[45]&0x0c)>>2] ,ReqStr[(hkpkt[45]&0x03) ] ,hkpkt[46]*256+hkpkt[47] ,hkpkt[48]*256+hkpkt[49] ,hkpkt[70]); sprintf(str2,"%s\t%5.2f\t%5.2f\t%5.2f\t%5.2f\t%5.2f\t%5.2f\t%5.2f\t%5.2f" ,str1 ,TempsCal(hkpkt[71]) ,TempsCal(hkpkt[72]) ,TempsCal(hkpkt[73]) ,TempsCal(hkpkt[74]) ,TempsCal(hkpkt[75]) ,TempsCal(hkpkt[76]) ,TempsCal(hkpkt[77]) ,TempsCal(hkpkt[78])); sprintf(str1,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d" ,str2 ,SafetyStr[(hkpkt[79]&0x20)>>5]// Safety ,SafetyStr[(hkpkt[79]&0x10)>>4] ,SafetyStr[(hkpkt[79]&0x08)>>3] ,SafetyStr[(hkpkt[79]&0x04)>>2] ,SafetyStr[(hkpkt[79]&0x02)>>1] ,SafetyStr[(hkpkt[79]&0x01) ] ,hkpkt[80]*256+hkpkt[81]); // Safety Timeout sprintf(str2,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" ,str1 ,OverrideStr[(hkpkt[82]&0x80)>>7] ,MaskedStr[(hkpkt[82]&0x20)>>5]// Safety Masks ,MaskedStr[(hkpkt[82]&0x10)>>4] ,MaskedStr[(hkpkt[82]&0x08)>>3] ,MaskedStr[(hkpkt[82]&0x04)>>2] ,MaskedStr[(hkpkt[82]&0x02)>>1] ,MaskedStr[(hkpkt[82]&0x01) ]); sprintf(str1,"%s\t%s\t%s\t%4.2f" ,str2 ,CodeStr[(hkpkt[83]&0xf0)>>4] // CodeStat ,HwStr[(hkpkt[83]&0x0f) ] // HW Version ,((hkpkt[84]&0xf0)>>4)+((hkpkt[84]&0x0f)/100.0) ); sprintf(str2,"%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t0x%04x" ,str1 ,(hkpkt[85]&0x80)>>7 // XTcStat ,(hkpkt[85]&0x40)>>6 ,(hkpkt[85]&0x20)>>5 ,(hkpkt[85]&0x10)>>4 ,(hkpkt[85]&0x08)>>3 ,(hkpkt[85]&0x04)>>2 ,(hkpkt[85]&0x02)>>1 ,(hkpkt[85]&0x01) ,hkpkt[86]*256+hkpkt[87] ); // Mem Check sprintf(str1,"%s\t%d\t%d\t0x%02x\t%d\t%d\t%s\t%d\t%d\t%d\t%d\t0x%04x" ,str2 ,hkpkt[88]*256+hkpkt[89] // Proc Idle ,hkpkt[90]*256+hkpkt[91] // Proc Sched ,hkpkt[92] // Test Stat ,hkpkt[103] // Min Stack ,hkpkt[104] // First Deleted ,SlowStr[(hkpkt[105]&0xe0)>>5] // Slow Stat ,(hkpkt[105]&0x10)>>4 // Watchdog >15 ,(hkpkt[105]&0x0f) // Watchdog cnt ,hkpkt[106] // Param Index ,hkpkt[107] // Param Value ,hkpkt[108]*256+hkpkt[109]); // Packet Check return(str1); } /* generateEngineeringFileString() */ /*-----------------------------------------------------------------------*/ void writeLogHeader(FILE *lfp, unsigned char hfil[],FILEHEADER file[]) /* ARGS file pointer, housekeeping files index, list of all file headers, RETURNS none DESCRIPTION Write the LAMP log file header to the specified ASCII log file. */ { unsigned i; char timstr[30]; fprintf(lfp,"# LAMP log file --------------------------------------------------------------------\n"); fprintf(lfp,"# ----------------------------------------------------------------------------------\n"); fprintf(lfp,"# %s\n", VERSIONSTR); fprintf(lfp,"# created: %s\n",currentUtcTimeStr(timstr)); fprintf(lfp,"# start: %23.3f, end %23.3f\n" ,realSUTC(file[hfil[0]].Start) ,realSUTC(file[hfil[strlen(hfil)-1]].Stop)); fprintf(lfp,"# start: %23s, end %23s\n# processed files:\n" ,utcTimeStr(file[hfil[0]].Start) ,utcTimeStr(file[hfil[strlen(hfil)-1]].Stop)); for (i=0;i----------------------------------------------------------------------*/ void logEvent(FILE *lfp, SUTC tim, const char str1[], const char str2[]) /* ARGS file pointer, RETURNS none DESCRIPTION Write the LAMP log file header to the specified ASCII log file. */ { fprintf(lfp,"%14.3f\t%17s\t%s%s\n",realSUTC(tim),utcTimeStr(tim),str1, str2); } /* logEvent() */ /*-----------------------------------------------------------------------*/ void processLogging(FILE *lfp, SUTC tim, BYTE hkpkt[]) /* ARGS file pointer, RETURNS none DESCRIPTION Monitor the received HK packet and write log file entries to the specified ASCII log file for important changes of status. */ { static BYTE sw= 0xff; // initially always trigger change static BYTE hw= 0xff; // initially always trigger change static int acccnt= -1; static int rejcnt= -1; static int execnt= 0; static BYTE acccmd= 0xff; static BYTE rejcmd= 0xff; static BYTE failcode= 0xfe; // reset code static BYTE opstate= 0; static BYTE safetyact= 0; static BYTE lastsafe= 0; static BYTE safetystat= 0; static BYTE safetymask= 0; static int frameno= 0x0fff; static BYTE apdoor= 255; // initially always trigger change static BYTE detdoor= 255; // initially always trigger change static BYTE stim= 255; // initially always trigger change static BYTE wpadrive= 0; static BYTE wpaswitch= 0; static BYTE lts= 0; static BYTE hvset= 0; static int memcheck= -1; // initially always trigger change static BYTE teststat= 0; static BYTE safecycles= 0; static BYTE discvolt= 255; // initially always trigger change static BYTE mirrset= 0; static BYTE gratset= 0; char str[50]; // platform and/or code change if ((sw!=((hkpkt[83]&0xf0)>>4))||(hw!=(hkpkt[83]&0x0f))) { sprintf(str,"LAFS version %4.2f from %s on %s" ,((hkpkt[84]&0xf0)>>4)+((hkpkt[84]&0x0f)/100.0) ,CodeStr[(hkpkt[83]&0xf0)>>4],HwStr[(hkpkt[83]&0x0f)]); logEvent(lfp,tim,"Executing: ",str); } sw= ((hkpkt[83]&0xf0)>>4); hw= (hkpkt[83]&0x0f); if ((acccnt>=0)&&(acccnt!=hkpkt[4]*256+hkpkt[5])) { if ((hkpkt[4]*256+hkpkt[5])==((acccnt+1)&0xffff)) { if ((hkpkt[8]*256+hkpkt[9])==((execnt+1)&0xffff)) { logEvent(lfp,tim,"Command accepted and executed: " ,CommandStr(hkpkt[10])); } else { logEvent(lfp,tim,"Command accepted: ",CommandStr(hkpkt[10])); } } else if ((hkpkt[10]==0xff)&&((hkpkt[4]*256+hkpkt[5])==0)) { logEvent(lfp,tim,"Command counter reset ","(power cycle?)"); } else { logEvent(lfp,tim,"Commands accepted, last command: " ,CommandStr(hkpkt[10])); } } else if ((hkpkt[8]*256+hkpkt[9])==((execnt+1)&0xffff)) { logEvent(lfp,tim,"Command execution completed",""); } else if (acccmd!=hkpkt[10]) { logEvent(lfp,tim,"Last accepted command changed: ",CommandStr(hkpkt[10])); } acccnt= hkpkt[4]*256+hkpkt[5]; execnt= hkpkt[8]*256+hkpkt[9]; acccmd= hkpkt[10]; // Command rejected and/or count changed if ((rejcnt>=0)&&(rejcnt!=hkpkt[6]*256+hkpkt[7])) { if ((hkpkt[6]*256+hkpkt[7])==((rejcnt+1)&0xffff)) { logEvent(lfp,tim,"Command rejected: ",CommandStr(hkpkt[11])); } else if ( ((acccnt!=hkpkt[6]*256+hkpkt[7])!=0) ||((acccnt!=hkpkt[4]*256+hkpkt[5])!=0) ) { logEvent(lfp,tim,"Commands rejected, last command: " ,CommandStr(hkpkt[11])); } } else if (acccmd!=hkpkt[10]) { logEvent(lfp,tim,"Last rejected command changed: ",CommandStr(hkpkt[11])); } rejcnt= hkpkt[6]*256+hkpkt[7]; rejcmd= hkpkt[11]; // Failcode changed if (acccmd!=hkpkt[10]) { logEvent(lfp,tim,"Failcode changed: ","errorcode"); } failcode= hkpkt[12]; // Safety Status changes or Last Safety changes if (((hkpkt[0]&0x08)!=0)&&(safetyact==0)) { logEvent(lfp,tim,"Safety raised: ",LastSafetyStr[(hkpkt[0]&0x07)]); } else if (((hkpkt[0]&0x08)==0)&&(safetyact!=0)) { sprintf(str,"(last: %s)",LastSafetyStr[(hkpkt[0]&0x07)]); logEvent(lfp,tim,"Safety cleared",str); } else if (lastsafe!=(hkpkt[0]&0x07)) { if ((hkpkt[0]&0x07)==0) { logEvent(lfp,tim,"Last Safety cleared",""); } else { logEvent(lfp,tim,"Last Safety changed: ",LastSafetyStr[(hkpkt[0]&0x07)]); } } safetyact= (hkpkt[0]&0x08)>>3; lastsafe= (hkpkt[0]&0x07); // safety / safety mask changed if (safetystat!=(hkpkt[79])) { sprintf(str,"0x%02x",hkpkt[79]); logEvent(lfp,tim,"Reported Safety status: ",str); } if (safetymask!=(hkpkt[82])) { sprintf(str,"0x%02x",hkpkt[82]); logEvent(lfp,tim,"Reported Safety mask: ",str); } safetystat= hkpkt[79]; safetymask= hkpkt[82]; // Opstate changed if ((opstate!=0)&&(opstate!=((hkpkt[0]&0x70)>>4))) { logEvent(lfp,tim,"Instrument state changed to: ",OpStatStr[(hkpkt[0]&0x70)>>4]); } opstate= ((hkpkt[0]&0x70)>>4); // science frame generated if (frameno!=((hkpkt[14]*256+hkpkt[15])&0xfff)) { if (((hkpkt[14]*256+hkpkt[15])&0xfff)==0xfff) { logEvent(lfp,tim,"Science frame number changes to 0xfff"," (reset?)"); } else { sprintf(str,"%s %s %s %s %04d",AcqStr[(hkpkt[14]&0x80)>>7] ,ABStr[(hkpkt[14]&0x40)>>6] ,LastStr[(hkpkt[14]&0x20)>>5] ,HwCtrlStr[(hkpkt[14]&0x10)>>4] ,(hkpkt[14]*256+hkpkt[15])&0xfff); logEvent(lfp,tim,"Science frame generated: ",str); } } frameno=((hkpkt[14]*256+hkpkt[15])&0xfff); // aperture door status change if (apdoor!=((hkpkt[16]&0x30)>>4)) { logEvent(lfp,tim,"Aperture door reported: ",ApDoorStr[(hkpkt[16]&0x30)>>4]); } apdoor= ((hkpkt[16]&0x30)>>4); // detector door status change if (detdoor!=((hkpkt[16]&0xc0)>>6)) { logEvent(lfp,tim,"Detector door reported: ",DetDoorStr[(hkpkt[16]&0xc0)>>6]); } detdoor= ((hkpkt[16]&0xc0)>>6); // WPA driven state changed if (wpadrive!=((hkpkt[1]&0x02)>>1)) { logEvent(lfp,tim,"WPA drive reported: ",OffOnStr[(hkpkt[1]&0x02)>>1]); } wpadrive= ((hkpkt[1]&0x02)>>1); // WPA switch state changed if (wpaswitch!=(hkpkt[1]&0x01)) { logEvent(lfp,tim,"WPA switch reported: ",ActiveStr[(hkpkt[1]&0x01)] ); } wpaswitch= (hkpkt[1]&0x01); // STIM status change if (stim!=(hkpkt[17]&0x01)) { logEvent(lfp,tim,"STIM state reported: ",StimStr[(hkpkt[17]&0x01)]); } stim= (hkpkt[17]&0x01); // Discriminator changes by at least 2 counts if ((discvolt!=hkpkt[44])&&(discvolt!=hkpkt[44]+1)&&(discvolt!=hkpkt[44]-1)) { sprintf(str,"%4.2f Volt",DiscrCal(hkpkt[44])); logEvent(lfp,tim,"Discriminator setpoint ",str); } discvolt= hkpkt[44]; // HvSet change if (hvset!=hkpkt[20]) { if (hkpkt[20]==0) { logEvent(lfp,tim,"HV turned off ","(zero set reported)"); } else { sprintf(str,"%3d = %6.3f kVolt (%s)",hkpkt[20],HvSetCal(hkpkt[20]) ,ABBothStr[(hkpkt[1]&0x30)>>4]); logEvent(lfp,tim,"HV commanded to ",str); } } hvset= hkpkt[20]; // MemCheck change if (memcheck!=(hkpkt[86]*256+hkpkt[87])) { if ((hkpkt[86]*256+hkpkt[87])==0) { logEvent(lfp,tim,"Checksum calculation started ","(zero reported)"); } else { sprintf(str,"0x%04x",(hkpkt[86]*256+hkpkt[87])); logEvent(lfp,tim,"Reported memory checksum changed: ",str); } } memcheck=(hkpkt[86]*256+hkpkt[87]); // Teststat change if (teststat!=hkpkt[92]) { if (hkpkt[92]==0) { logEvent(lfp,tim,"Reported Test Status: ","cleared"); } else { sprintf(str,"0x%02x",hkpkt[92]); logEvent(lfp,tim,"Reported Test Status: ",str); } } teststat= hkpkt[92]; // LTS status or safe cycles change if (lts!=((hkpkt[16]&0x04)>>2)) { logEvent(lfp,tim,"LTS state: ",LtsDarkStr[(hkpkt[16]&0x04)>>2]); } lts= ((hkpkt[16]&0x04)>>2); if (safecycles!=hkpkt[70]) { sprintf(str,"%d",hkpkt[70]); logEvent(lfp,tim,"Reported LTS safe cycles: ",str); } safecycles= hkpkt[70]; // any heater setpoint change if ((mirrset!=hkpkt[71])||(gratset!=hkpkt[72])) { if (hkpkt[71]==0) { sprintf(str,"mirr. %5s, ","off"); } else { sprintf(str,"mirr. %5.1f, ",TempsCal(hkpkt[71])); } if (hkpkt[72]==0) { sprintf(&str[12]," grat. %5s","off"); } else { sprintf(&str[12]," grat. %5.1f",TempsCal(hkpkt[72])); } logEvent(lfp,tim,"Heater setpoint change: ",str); } mirrset= hkpkt[71]; gratset= hkpkt[72]; // ToDo more ?? } /* processLogging() */ /*-----------------------------------------------------------------------*/ int generateEngineeringOutput(int verbose, char params, char logging , unsigned char hfil[],FILEHEADER file[] , char edir[], HKOVERVIEW *hovr) /* ARGS verbose status, parameter dump option, logging option, housekeeping files index, list of all file headers, engineering data directory name (output), hk overview structure RETURNS none DESCRIPTION This function performs the second scan of all the sorted housekeeping files and generates the engineering output file, to generate this file the hack- clock correlation is needed to generate the most accurate timing information, so that is why a separate earlier scan was needed to establish this hack-clock correlation. For each housekeeping packet in the housekeeping input file(s), a single line is added to the engineering output file, with the specified entries, preceded by the SCUT in seconds and normal time format. Each line consists of a tab character separated list of all field in the housekeeping packet converted into engineering format (where applicable), except for the high rate LTS data since this data is sampled at a 10 Hz rate and thus doesn't fit the nominal 1 Hz housekeeping data. For a verbose level of 3 or above this information is also output onto standard output. This function also processes the memory dump packets and generates a single binary dump file of all consecutive memory dump sequences. In addition it will also generate the ASCII representations of the 'special' memory dumps of the three know data structure: error log, parameter dump and door life test results. The function decommutates the parameter index and value as reported in the housekeeping stream and if enabled it will generate a parameter dump file whenever an initial complete set of parameters has been received or when one of the reported parameters changes value. These parameter dump files may be used for instrument operations and trend monitoring. If the logging function is enabled an additional log output file will be generated (with a base name identiccal to the engineering file) that provides an intellignet log of the LAMP reported activities. It is limited version of the logging system that is provided by the GSE. */ { unsigned i,j; FILE *hkfp; FILE *efp; FILEHEADER hdr; char ok = 1; int mdpktcnt= -1; CCSDSHEADER ccsds; SUTC stim; BYTE hkpkt[LAMPHKPKTLEN-5]; char filename[MAXFILENAME]; char engstr[700]; // !! WARNING debugger will only show first 256 characters int hacktime= 0; BYTE parameter[LAMPPARAMETERS]; char captured[LAMPPARAMETERS];// parameter captured int missing= LAMPPARAMETERS; // number of parameters not received yet BYTE mdpkt[LAMPMDPKTLEN-5]; LONG memaddr; WORD membyts; BYTE memtype; LONG nextaddr= -1; BYTE lasttype= 0xff; FILE *mfp; char mfopen= 0; char logname[MAXFILENAME]; FILE *lfp; char logstr[30]; resetHackWrap(); for (i=0;i1) { printf("## file %s\n",filename); } if (logging) { makeOutputFileName(file[1].Start.sec,edir,LOGEXTEND,logname); if ((lfp=fopen(logname,FMODEWRITE))!=NULL) { printf("# generate log output file: %s\n",logname); writeLogHeader(lfp,hfil,file); } else { printf("FAILED to open log file, logging function disabled\n"); logging= 0; } } if ((efp=fopen(filename,FMODEWRITE))!=NULL) { writeEngineeringHeader(efp, verbose); // Generate file header for (i=0;i2) { printf("### Hk %d, index %d: %s\n",i,hfil[i],file[hfil[i]].realname); } if ((hkfp = fopen(file[hfil[i]].realname,FMODEREAD))!=NULL) { if (readFileHeader(hkfp,&hdr)) { while ((!feof(hkfp))&&(ok)) { if (readCcsdsHeader(hkfp,&ccsds)) { if (ccsds.apid==LAMPHKAPID) { if (ccsds.pktlen!=LAMPHKPKTLEN) { printf("ERROR: unexpected HK packet size %d\n",ccsds.pktlen); ok= 0; } readLONG(hkfp,&(stim.sec)); readWORD(hkfp,&(stim.sub)); readString(hkfp,hkpkt,ccsds.pktlen-5); // sec & sub already read hacktime= unwrappedHackTime(hkpkt[24],hkpkt[25]); writeToEngineering(efp,verbose ,generateEngineeringFileString(hacktime,stim,ccsds.pktcnt ,hovr->htf,hkpkt,engstr)); if (logging) { processLogging(lfp,stim,hkpkt); } /* parameter value decommutation */ //ToDo (?) Reset parameter memory status on power cycle or reboot // to also handle power cycle situations correctly, it // is assumed that this doesn't happen and that a new // file is started after a power cyle. if (params) // extended parameter option { if (hkpkt[106]0) // not all parameters seen yet { if ((captured[hkpkt[106]])&&(parameter[hkpkt[106]]!=hkpkt[107])) { printf("WARNING: early parameter change skipped (%d at SCUT %14.3f)\n" ,hkpkt[106],realSUTC(stim)); } else { parameter[hkpkt[106]]= hkpkt[107]; // store param value captured[hkpkt[106]]= 1; // signal stored if ((--missing)==0) { saveWorkParamDump(stim,edir,parameter,"hk decom initial"); } } } else // all parameters seen at least once { if (parameter[hkpkt[106]]!=hkpkt[107]) // if changed { parameter[hkpkt[106]]= hkpkt[107]; // store new value saveWorkParamDump(stim,edir,parameter,"hk decom change"); } } } else { printf("ERROR: parameter index out of range (%d at SCUT %14.3f)\n" ,hkpkt[106],realSUTC(stim)); } } } else if (ccsds.apid==LAMPMDAPID) { if (verbose>2) { printf("Memory Dump packet ApId %d, len %d\n" ,ccsds.apid,ccsds.pktlen); } if (ccsds.pktlen!=LAMPMDPKTLEN) { printf("WARNING: unexpected MD packet size %d\n" ,ccsds.pktlen); ok= 0; } readLONG(hkfp,&(stim.sec)); readWORD(hkfp,&(stim.sub)); readString(hkfp,mdpkt,ccsds.pktlen-5); // sec & sub already read if ((mdpktcnt!=-1)&&(ccsds.pktcnt!=((mdpktcnt+1)&0x3fff))) { printf("WARNING: MD packet count discontinuity %d after %d (SCUT %14.3f)\n" ,ccsds.pktcnt,mdpktcnt,realSUTC(stim)); } mdpktcnt= ccsds.pktcnt; memaddr= (mdpkt[0]<<24)+(mdpkt[1]<<16)+(mdpkt[2]<<8)+mdpkt[3]; membyts= (mdpkt[4]<<8)+mdpkt[5]; memtype= mdpkt[6]; if (logging) { sprintf(logstr,"0x%02x 0x%04x %d",memtype,memaddr,membyts); logEvent(lfp,stim,"Memory Dump Packet: ",logstr); } if ((memtype!=lasttype)||(memaddr!=nextaddr)) { /* start new file */ if (mfopen) { fclose(mfp); mfopen= 0; } makeMemDumpFileName(stim.sec,edir,memtype,memaddr,filename); printf("# create dump file: %s\n",filename); if ((mfp=fopen(filename,FMODEWRITE))!=NULL) { mfopen= 1; } else { printf("ERROR: failed to create dump file: %s\n",filename); } } if (mfopen) { for(j=0;j----------------------------------------------------------------------*/ LONG mergeHackTimes(LONG fullhack, LONG partial) /* ARGS full hack time (>=16 bits data), partial hack time (15 bits data) RETURNS merged value of the partial hack time DESCRIPTION This function aligns a partial hack time (with assumed higher accuracy) with the general hacktime which has full resolution (>= 16 bits). The partial hack time results from a timehack where only 15 bits of hack information is know and this needs to be resolved (aligned) with a hacktime that is unrolled to remove rollovers from the limited hack clock. The merging process recognizes three different cases: - top bits of the partial hack time and full hack time are equal and the top two bits of the partial hack are either 01 or 10, then the partial hack time can just replace the lower part of the full hack time. - top bits not equal or partial hack top bits 11 or 00 and full hack bit 15 is 1, then merge lower parts and add 0x8000 for a roll over - neither of these, then merge lower parts and subtract 0x8000 for a roll-back */ { if ( ((fullhack&0x4000)==(partial&0x4000)) ||((partial&0x6000)==0x4000) ||((partial&0x6000)==0x2000) ) { return((fullhack&(~0x7fff))+(partial&0x7fff)); } else if (fullhack&0x4000) { return((fullhack&(~0x7fff))+(partial&0x7fff)+0x008000); } else { return((fullhack&(~0x7fff))+(partial&0x7fff)-0x008000); } } /* mergeHackTimes(() */ /*-----------------------------------------------------------------------*/ int calculateHistograms(char verbose, char correct, unsigned char sfil[] , FILEHEADER file[], ACQLIST *acq, IMAGE doh, IMAGE dch , SCIOVERVIEW *sovr, HKOVERVIEW *hovr) /* ARGS verbose status, correct hack error option, science files index, list of all file headers, acquisition list, door open image (output), door close image (output), science overview structure (output), hk overview structure RETURNS 1 - processing completed successfully DESCRIPTION This is the first scan over all the acquisitions in the science file(s), during this scan the data in the science file are checked and a link between the acquisitions in the acquisition list and the actual science acquisitions is established. If no entry in the acquisition list is found then a science based entry is added (this is useful as it allows LIMA to process science files for which no housekeeping files are availble. For pixellist acquisitions this correlation also allows for the determination of the acquisition duration with a higher accuracy based on the observed time hacks in the science data. After resolving the science timehack with the unrolled timehack the start and stop times in the acquisition list are updated with the new (more accurate) numbers. During pixellist acquisition scans this function also checks the timehack values found and it will report when incorrect (unexpected time values) timehacks are found and continue working with the corrected value. This scan does not write the acquisition data to the FITS file so the corrected values are again lost after this scan. The final corrections of the timehack values that are saved in the result FITS file are recreated in a later scan. If all acquisitions in the science file(s) are all pixellists then overview images (histograms) are created to aid the first order inspection of the results. Separate overview images are made for the door open and door close case. Histograms found in the science file(s) though will take precedence and the first and second histogram file will replace these overview images. For all the received frames and possibly the overview histograms a checksum based identification is performed. This process calculates a 24 bit checksum and compares it against a set of know checksums for the LAMP test patterns. Although this check is not perfect, it will always recognize the test patterns and only misclassify actual dat in a few cases (probability <10-6). The function initializes the science overview structure and populates it with values form the series of science frames scanned. From the housekeeping overview this function only uses the hack correlation information to establish timing information for the acquistions, if the input data only consists of science data (no house keeping packets) then this will be detected here and a ideal default time correlation will be established and used. */ { unsigned i,si; FILE *fp; FILEHEADER hdr; char ok = 1; int acquit= -1; SUTC stim; WORD ident,w,fix; LONG *lptr; int lasthack= -1; int firsthack= -1; int hackrate; int cstart; int cstop; int duration; double tim; WORD events=0; LONG cr; double dtime; double acqtime; char firstframe= 1; char processing; int fixone; memset(doh,0,sizeof(IMAGE)); memset(dch,0,sizeof(IMAGE)); memset(sovr,0,sizeof(SCIOVERVIEW)); sovr->domincr= MAXLONG; sovr->dcmincr= MAXLONG; sovr->mincr= MAXLONG; sovr->crtime= 0.0; sovr->mincrdo= MAXLONG; sovr->mincrdc= MAXLONG; printf("# collect detailed information from the %d science file(s)\n",strlen(sfil)); for (si=0;si1) { printf("## Sci %d %d %s (mode %s) ##\n",si,sfil[si],file[sfil[si]].realname,FMODEREAD); } if ((fp = fopen(file[sfil[si]].realname,FMODEREAD))!=NULL) { if (readFileHeader(fp,&hdr)) { readLONG(fp,&(stim.sec)); // pre-read so file status will show eof() while ((!feof(fp))&&(ok)) { acquit++; readWORD(fp,&(stim.sub)); tim= realSUTC(stim); readDROW(fp,&ident); if (verbose>2) { printf("### frame 0x%04x time %s (%14.3f)\n",ident,utcTimeStr(stim),realSUTC(stim)); } if (ident&0x8000) { // histogram, copy data to an image slot if still availalble and update keywords sovr->hisacq++; if (sovr->hisacq<=2) { lptr= (sovr->hisacq==1)?&doh[1]:&dch[1]; for (i=1;i<32768;i++) { readDROW(fp,&w); *lptr++= w; } if (sovr->hisacq==1) { if (verbose>1) { printf("## first histogram data found, copy to primary (%d)\n",sovr->hisacq); } sovr->dopixacq= 0; sovr->dohacks= -1; sovr->dotime= acq->ac[acquit].stop-acq->ac[acquit].start; sovr->domincr= acq->ac[acquit].mincr; sovr->domaxcr= acq->ac[acquit].maxcr; sovr->dolts= acq->ac[acquit].ltsstate; sovr->dostims= acq->ac[acquit].stimstate; sovr->dohvps= acq->ac[acquit].hvpsstate; } else { if (verbose>1) { printf("## second histogram data found, copy to extension 1 (%d)\n",sovr->hisacq); } sovr->dcpixacq= 0; sovr->dchacks= -1; sovr->dctime= acq->ac[acquit].stop-acq->ac[acquit].start; sovr->dcmincr= acq->ac[acquit].mincr; sovr->dcmaxcr= acq->ac[acquit].maxcr; sovr->dclts= acq->ac[acquit].ltsstate; sovr->dcstims= acq->ac[acquit].stimstate; sovr->dchvps= acq->ac[acquit].hvpsstate; } } else // just skip { if (verbose>1) { printf("## %dth histogram data found, just skip\n",sovr->hisacq); } for (i=1;i<32768;i++) { readDROW(fp,&w); } } } else { // pixellist. split processing in three cases depending on aperture door state, // this can only be done if a matching acquisition was found in HK data, and // the appropriate histogram slot was not yet taken, otherwise just treat as // variable aperture door position and don't sum into histogram if ( (tim>=acq->ac[acquit].start)&&(tim<=acq->ac[acquit].stop+HSDATATIMELIMIT) &&( ((acq->ac[acquit].apdstate==2)&&(sovr->hisacq<=1)) ||((acq->ac[acquit].apdstate==3)&&(sovr->hisacq==0)) ) ) { processing= acq->ac[acquit].apdstate; // 3 open door, 2 close door, 4 variable } else { processing= 4; // variable, or no acquisition entry } if (verbose>1) { if (processing==2) { printf("## Pixellist Door Close Histogramming (day side)\n"); } else if (processing==3) { printf("## Pixellist Door Open Histogramming (dark side)\n"); } else // (processing==4) { printf("## Pixellist skip histogramming (%d %d %d)\n",acq->acquisitions ,acq->ac[acquit].apdstate,sovr->hisacq); } } firsthack= -1; lasthack= -1; hackrate= -1; acqtime= 0.0; sovr->pixacq++; for (i=1;i<32768;i++) { readDROW(fp,&w); if (w&0x8000) { // timehack w&= 0x7fff; sovr->hacks++; if (processing==2) { sovr->dchacks++; } else if (processing==3) { sovr->dohacks++; } if (firsthack<0) { firsthack= w; if (firstframe) // firsthack & firstframe { // if no hack correlation defined fake one based on science file time if (hovr->htf[0].hackratehtf[0].hackrate= NOMINALHACKCLOCK; hovr->htf[0].hackoffset= realSUTC(hdr.Start)-(hovr->htf[0].hackrate*w); hovr->htf[0].firsthack= 0; hovr->htf[0].lasthack= 0x7fffffff; } firstframe= 0; } } if ((lasthack>=0)&&(w!=lasthack-1)) // exclude decrementing test pattern { if (hackrate<0) { // attempt to calculate hackrate once two times are available hackrate= calculateHackrate(w-lasthack); fixone= 1; // add one hack for the first time in a frame } // note the hackrate starts numbering here at 1 (= 4ms) else { fixone= 0; } if (hackrate>0) // once correct hackrate has been established { if ((correct)&&(((w-lasthack+0x8000)%0x8000)!=(1<<(hackrate-1)))) { // try to fix the problem (simple way!!) fix= (lasthack+(1<<(hackrate-1)))&0x7fff; printf("# NOTE: acq %d: fixed hack step (from 0x%04x to 0x%04x at %d)\n" ,acquit+1,w,fix,i); w= fix; // apply the fix sovr->hackfixes++; } if (((w-lasthack+0x8000)%0x8000)!=(1<<(hackrate-1))) { printf("WARNING: acq %d: inconsistent hack step (0x%04x after 0x%04x at %d)\n" ,acquit+1,w,lasthack,i); } dtime= ((w-lasthack+0x8000)%0x8000)*NOMINALHACKCLOCK; acqtime+= dtime+fixone*NOMINALHACKCLOCK; sovr->crtime+= dtime+fixone*NOMINALHACKCLOCK; cr= (long)(events/dtime); minmax(cr,sovr->mincr ,sovr->maxcr ); if (acq->ac[acquit].apdstate==2) { sovr->evtcrdc+= events; sovr->timecrdc+= dtime+fixone*NOMINALHACKCLOCK; minmax(cr,sovr->mincrdc,sovr->maxcrdc); } else if (acq->ac[acquit].apdstate==3) { sovr->evtcrdo+= events; sovr->timecrdo+= dtime+fixone*NOMINALHACKCLOCK; minmax(cr,sovr->mincrdo,sovr->maxcrdo); } if (processing==2) { sovr->dctime+= dtime+fixone*NOMINALHACKCLOCK; } else if (processing==3) { sovr->dotime+= dtime+fixone*NOMINALHACKCLOCK; } } } lasthack= w; events= 0; } else { // photon event sovr->pixevents++; events++; if (processing==2) { dch[w]++; } else if (processing==3) { doh[w]++; } } } // for all pixellist entries if (processing=2) { sovr->dcpixacq++; statechange(sovr->dclts,acq->ac[acquit].ltsstate); statechange(sovr->dcstims,acq->ac[acquit].stimstate); statechange(sovr->dchvps,acq->ac[acquit].hvpsstate); minmax(acq->ac[acquit].mincr,sovr->dcmincr,sovr->dcmaxcr); minmax(acq->ac[acquit].maxcr,sovr->dcmincr,sovr->dcmaxcr); } else if (processing=3) { sovr->dopixacq++; statechange(sovr->dolts,acq->ac[acquit].ltsstate); statechange(sovr->dostims,acq->ac[acquit].stimstate); statechange(sovr->dohvps,acq->ac[acquit].hvpsstate); minmax(acq->ac[acquit].mincr,sovr->domincr,sovr->domaxcr); minmax(acq->ac[acquit].maxcr,sovr->domincr,sovr->domaxcr); } else { if (acq->ac[acquit].start<0.0) { /* try to reconstruct acquisition info based on science */ acq->ac[acquit].start= (acq->ac[acquit].stop)-acqtime; acq->ac[acquit].hackrate= hackrate; acq->ac[acquit].mincr= (WORD)sovr->mincr; acq->ac[acquit].maxcr= (WORD)sovr->maxcr; acq->ac[acquit].hackstart= reconstructedHack(acq->ac[acquit].start, hovr->htf); acq->ac[acquit].hackstop= reconstructedHack(acq->ac[acquit].stop, hovr->htf); } } if (firsthack>=0) { // correct start & stop based on hack times // Only attempt corrections for acquisitions that were found in HK data if (acq->ac[acquit].scihdr!=NOHKHEADER) { cstart= mergeHackTimes(acq->ac[acquit].hackstart,firsthack); cstop= mergeHackTimes(acq->ac[acquit].hackstop ,lasthack ); duration= (lasthack-firsthack)&0x7fff; // adjust to middle of hack period: // start expected to be equal to stop of previous if (acq->ac[acquit].hackrate>1) { cstart-= (1<<(acq->ac[acquit].hackrate-2)); cstop+= (1<<(acq->ac[acquit].hackrate-2)); } else { cstart-= 1; } if (verbose>3) { printf("#### acquisition %3d hack time correction: frame %d, %d\n" ,acquit+1,acq->ac[acquit].frameno,ident&0xfff); printf("#### start %14.3f %6lx %14.3f %04x %6lx %14.3f\n" ,acq->ac[acquit].start ,acq->ac[acquit].hackstart ,reconstructedSUTC(acq->ac[acquit].hackstart,hovr->htf) ,firsthack&0x3fff ,cstart ,reconstructedSUTC(cstart,hovr->htf)); printf("#### stop %14.3f %6lx %14.3f %04x %6lx %14.3f\n" ,acq->ac[acquit].stop ,acq->ac[acquit].hackstop ,reconstructedSUTC(acq->ac[acquit].hackstop,hovr->htf) ,lasthack&0x3fff ,cstop ,reconstructedSUTC(cstop,hovr->htf)); } // Only apply correction if difference is small enough, this should also cover the case // of test pattern acquisitions as these are generated with fixed timehack values that // are not based on actual acquisition time. if (flabs(acq->ac[acquit].stop-reconstructedSUTC(cstop,hovr->htf))>MAXHACKCORRECTION) { printf("WARNING: acq %d: hack stop time correction too large: %d,%6.3f sec %\n" ,acquit+1 ,acq->ac[acquit].hackstop-cstop ,reconstructedSUTC(cstop,hovr->htf)-(acq->ac[acquit].stop)); } else { acq->ac[acquit].hackstop= cstop; } if (flabs(acq->ac[acquit].start-reconstructedSUTC(cstart,hovr->htf))>MAXHACKCORRECTION) { if ( (acquit==0) &&(((acq->ac[acquit].hackstop-acq->ac[acquit].hackstart)&0x7fff)ac[acquit].hackstart= acq->ac[acquit].hackstop-duration; printf("WARNING: first science frame started before HK start, estimated %14.3f\n" ,reconstructedSUTC(acq->ac[acquit].hackstart,hovr->htf)); } else { printf("WARNING: acq %d: hack start time correction too large: %d,%6.3f sec\n" ,acquit+1 ,acq->ac[acquit].hackstart-cstart ,reconstructedSUTC(cstart,hovr->htf)-(acq->ac[acquit].start)); } } else { acq->ac[acquit].hackstart= cstart; } } else /* no HK match found for science acquisition frame */ { // guess start time based on number of hacks acq->ac[acquit].hackstop= reconstructedHack(acq->ac[acquit].stop,hovr->htf); acq->ac[acquit].hackstart= acq->ac[acquit].hackstop-((lasthack-firsthack)&0x7fff); if (verbose) { printf("## Science only timing %d %14.3f %14.3f\n" ,acquit,reconstructedSUTC(acq->ac[acquit].hackstart,hovr->htf) ,reconstructedSUTC(acq->ac[acquit].hackstop,hovr->htf)); } } } else { printf("WARNING: acq %d, frame #%d: no firsthack found (frame corrupt?)\n" ,acquit+1,acq->ac[acquit].frameno); } } // pixellist if (feof(fp)) { printf("ERROR: file ended too early %s\n",file[sfil[si]].realname); } else { readLONG(fp,&(stim.sec)); // pre-read so file status will show eof() } } } else { printf("ERROR: file too small (<64): %s\n",file[sfil[i]].realname); ok= 0; } fclose(fp); } else { printf("ERROR: cannot access input file: %s\n",file[sfil[i]].realname); ok= 0; } } if (verbose) { printf("# Science adjustment completed: %d frames processed\n",acquit+1); } sovr->dochecksum= CHECKSUMSEED; for (i=1;i<32768;i++) { if (doh[i]domincnt) { sovr->domincnt= doh[i]; } if (doh[i]>sovr->domaxcnt) { sovr->domaxcnt= doh[i]; } if ((sovr->dopixacq!=0)||(i>2048+31)||((i&1023)>31)) { // exclude PHD events sovr->doevts+= doh[i]; } sovr->dochecksum= updateChecksum(sovr->dochecksum,doh[i]); } sovr->dcchecksum= CHECKSUMSEED; for (i=1;i<32768;i++) { if (dch[i]dcmincnt) { sovr->dcmincnt= dch[i]; } if (dch[i]>sovr->dcmaxcnt) { sovr->dcmaxcnt= dch[i]; } if ((sovr->dopixacq!=0)||(i>2048+31)||((i&1023)>31)) { // exclude PHD events sovr->dcevts+= dch[i]; } sovr->dcchecksum= updateChecksum(sovr->dcchecksum,dch[i]); } return(ok); } /* calculateHistograms() */ /*-----------------------------------------------------------------------*/ int processLampFiles(char verbose, char correct, char params, char logging ,char ddir[], char edir[], char targetfile[] ,unsigned char sfil[], unsigned char hfil[], FILEHEADER file[]) /* ARGS verbose status, hack correct status, param decom status, logging status, data directory name, engineering directory name, target file specification (name), science files index, housekeeping files index, list of all file headers RETURNS none DESCRIPTION This function calls the main processing steps of LAMP-LIMA for the LAMP telemetry files. First it willlist the input files on standard output, in the order in which they will be processed, this listing includes the start and stop times form the headers of the files. Then it calls the first and second housekeeping scan followed by the science data scan and finally the FITS file generation scan. This last scan will rescan both science and housekeeping files when needed to generate the FITS file: - 1st HK scan: find acquisitions from HK data and generate hk overview - 2nd HK scan: generate engineering output - science scan: calculate the overview histograms and science overview. These three scans generate all the overview information needed before the FITS file generation can be started and at this point it is know which files will need to be rescanned to produce data to include in the FITs file. */ { unsigned i; HKOVERVIEW hkover; ACQLIST acqu; SCIOVERVIEW sciover; IMAGE DoHistogram; IMAGE DcHistogram; acqu.acquisitions= 0; for (i=0;i0) )) &&(generateEngineeringOutput(verbose,params,logging,hfil,file,edir,&hkover)) &&(calculateHistograms(verbose,correct,sfil,file ,&acqu,DoHistogram,DcHistogram,&sciover,&hkover)) &&(generateFitsFile(verbose,correct,hfil,sfil,file,ddir,targetfile ,&hkover,&acqu,&sciover,DoHistogram,DcHistogram)) ) { return(1); } else { return(-3); } } /* processLampFiles() */ /*-----------------------------------------------------------------------*/ int processScHkFile(int verbose, char filename[], FILE *tfp, FILE *pfp, FILE *mfp) /* ARGS filename, three output file pointers to the generated data files RETURNS 1 if processing completed successfully DESCRIPTION Process a one or more LRO spacecraft HK file and generate the corresponding engineering output files. The function scans over all the CCSDS telemetry packets in the input file(s), and when an LAMP relevant S/C telemetry file is detected the appropriate fields written into the corresponding S/C engineering telemetry output file. The calling function already created these S/C engineering files and this function can just write a single string entry to such a file when a matching packet is found. This function contains the full code to generate these complete entries. LAMP has selected five separate LRO telemetry packets that contain LAMP related S/C telemetry: - ApId 106 - LRO Temperature telemetry packet - ApId 100 - LRO Power telemetry packet - ApId 32 - LRO Instrument Manager telemetry packet (SWIM) - ApId 48 - LRO SWIM debug telemetry Packet - skip - ApId 101 - LRO Power Debug telemetry Packet - skip For the time being this program only generates output upon detecting the first three packet, the latter two are simply skipped. The function will report a warning if other telemetry packets are detected in the input stream. The function generates the complete engineering (text) entries for the relevant telemetry values after converting these to engineering units (where applicable). */ { FILE *hkfp; char ok = 1; LONG time= -1; CCSDSHEADER ccsds; SUTC stim; BYTE hkpkt[MAXLROPKTLEN-5]; int ref; char timstr[50]; char engstr[120]; double cura; double curb; double volt; if ((hkfp = fopen(filename,FMODEREAD))!=NULL) { if (verbose>2) { printf("# process SCHK file: %s\n",filename); } while ((!feof(hkfp))&&(ok)) { if (readCcsdsHeader(hkfp,&ccsds)) { if (ccsds.pktlen<=MAXLROPKTLEN) { readLONG(hkfp,&(stim.sec)); readWORD(hkfp,&(stim.sub)); readString(hkfp,hkpkt,ccsds.pktlen-5); // sec & sub already read sprintf(timstr,"%14.3f\t%s\t%d",stim.sec+stim.sub/65536.0 ,utcTimeStr(stim) ,ccsds.pktcnt); if (ccsds.apid==LROTEMPAPID) { if (ccsds.pktlen==LROTEMPPKTLEN) { ref= HkPktWrd12(498); sprintf(engstr,"%s\t%4.2f\t%4.2f\t%4.2f",timstr ,ScTempsCal(HkPktWrd12(446),ref) ,ScTempsCal(HkPktWrd12(482),ref) ,ScTempsCal(HkPktWrd12(484),ref)); writeToEngineering(tfp,verbose,engstr); } else { printf("ERROR: unexpected LRO-temps packet size %d\n",ccsds.pktlen); ok= 0; } } else if (ccsds.apid==LROPOWRAPID) { if (ccsds.pktlen==LROPOWRPKTLEN) { cura= ScCurrentCal(HkPktWrd16( 94)); curb= ScCurrentCal(HkPktWrd16( 60)); volt= ScVoltageCal(HkPktWrd16(172)); sprintf(engstr,"%s\t%1.0f\t%1.0f\t%1.0f\t%4.2f\t%4.2f\t%s\t%s\t%s\t%s",timstr ,cura*1000,curb*1000,(cura+curb)*1000 ,volt,(cura+curb)*volt ,ScOffOnStr[HkPktBit(242,5)],ScOffOnStr[HkPktBit(240,4)] ,ScErrorStr[HkPktBit(256,5)],ScErrorStr[HkPktBit(254,4)]); writeToEngineering(pfp,verbose,engstr); } else { printf("ERROR: unexpected LRO-power packet size %d\n",ccsds.pktlen); ok= 0; } } else if (ccsds.apid==LROSWIMAPID) { if (ccsds.pktlen==LROSWIMPKTLEN) { sprintf(engstr,"%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d",timstr ,ScEnableStr[HkPktBit(21,1)] ,ScOpenStr[HkPktBit(23,2)] ,HkPktWrd16(136),HkPktWrd16(138) ,HkPktWrd16(140),HkPktWrd16(142) ,HkPktWrd32(144),HkPktWrd32(148) ,HkPktWrd32(152),HkPktWrd32(156) ,HkPktWrd32(160),HkPktWrd32(164) ,HkPktWrd32(168)); writeToEngineering(mfp,verbose,engstr); } else { printf("ERROR: unexpected LRO-swim packet size %d\n",ccsds.pktlen); ok= 0; } } else if (ccsds.apid==LROSWIDAPID) { // for the time being just ignore this SWIM Debug packet } else if (ccsds.apid==LROPWRDAPID) { // for the time being just ignore this Power Debug packet } else { printf("WARNING: unexpected ApId in LRO LAMP SC data, ApId %d, len %d\n" ,ccsds.apid,ccsds.pktlen); } } else { printf("WARNING: skipping excessive long packet ApId %d, len %d\n" ,ccsds.apid,ccsds.pktlen); skipBYTEs(hkfp,ccsds.pktlen+1); } } else if (!feof(hkfp)) { printf("ERROR: incorrect CCSDS header near %d in file %s\n" ,ftell(hkfp),filename); ok= 0; } } fclose(hkfp); } else { printf("ERROR: cannot access input file: %s\n",filename); ok= 0; } return(ok); } /* processScHkFile() */ /*-----------------------------------------------------------------------*/ int processLroFiles(char verbose, char edir[] , unsigned char lfil[], FILEHEADER file[]) /* ARGS verbose status, engineering directory name, lro hk files index, list of all file headers RETURNS none DESCRIPTION This function creates the three spacecraft telemetry output files for the ApIds 0x20, 0x64 and 0x6a based on the start time of the first file (actually the first data packet in the first file, since that is all that's available), writes the appropriate headers to the files and calls the process function processScHkFile() for each of the spacecraft telemetry files to be processed. After this the three output files are closed. Note that this will even generate an output file if no data is available for the specified telemetry packet. */ { unsigned i; char filename[MAXFILENAME]; char timstr[30]; char genby[80]; char genon[80]; FILE *tfp= NULL; FILE *pfp= NULL; FILE *mfp= NULL; int ok=0; for (i=0;i----------------------------------------------------------------------*/ void sortFiles(unsigned char fi[], FILEHEADER file[]) /* ARGS list of file indices (string), list of file headers RETURNS none DESCRIPTION Sort the list of file indices that are pointing to entries in the file header list so the start times listed in the file header list are in incrementing order. This function is needed when processing multiple input files for which the file names may not be in order of incrementing start times. The list of file indices is stored as a string, this makes the end detection of the list very easy as the standard '\0' string termination can be used, but it limits the maximum number of input files (hk + sci + sc) to 255, which seemed an acceptable design limitation. The function performs a simple exchange sort that is quick enough since the number of files to be sorted is small. */ { unsigned i,j; char tmp; if (strlen(fi)>1) { for (i=0;i----------------------------------------------------------------------*/ void printHelpText( void ) /* DESCRIPTION Print help text with parameter options */ { printf("# Program activation command and options\n"); printf("# lamp-lima \n"); printf("# [-v[#]] - verbose\n"); printf("# [-d] - data directory\n"); printf("# [-e] - engineering directory\n"); printf("# [-t] - target file\n"); printf("# [-c] - print calibration table\n"); printf("# [-h] - disable hack correction\n"); printf("# [-p] - parameter decommutation\n"); printf("# [-l] - generate (command) log file\n"); } /* printHelpText() */ /*-----------------------------------------------------------------------*/ main(int argc, char *argv[]) /* ARGS number of command line arguments, command line arguments RETURNS 1 - processing completes without problems, -1 - no processing, incorrect command line, -2 - no processing, input file(s) access failed, -3 - internal error DESCRIPTION This is the main entry point in the LAMP-LIMA processing function, the main function parses the parameters, creates the list of file headers (the index lists), sorts these lists and finally calls the appropriate processing functions processLampFiles() and/or processLroFiles() to perform the complete processing of all the input files. For any recognized parameter the selected option is reported to standard output as a comment entry (starting with '#'). To create the list of file headers of the input files and determine the type of the different files, all files are opened here once so this implicitly ensures that all the specified input files exist. Due to the fact that the LRO spacecraft telemetry files (no longer) include a standard header the check for these files requires that the file at least contains a single telemetry packet, the program will fail and report an error (file too small) if an empty spacecraft telemetry file is presented for processing. */ { char ok= 1; char timstr[30]; int ac; int files= 0; FILE *fp; char verbose= 0; // controls verbose option setting char correct= 1; // controls sci-hack correction option char params= 0; // control parameter reporting option char logging= 0; // log fiel generation option char destdir[MAXDIRNAME]= "."; char engdir[MAXDIRNAME]= "."; char targetfile[MAXDIRNAME]= TARGETFILE; unsigned char scifiles[MAXFILES]= ""; unsigned char hkfiles[MAXFILES]= ""; unsigned char scfiles[MAXFILES]= ""; FILEHEADER file[MAXFILES]; SUTC time; if ((argc>1)&&(argc2) ) { strcpy(destdir,&argv[ac][2]); printf("# destination directory: %s\n",destdir); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='e') &&(strlen(argv[ac])>2) ) { strcpy(engdir,&argv[ac][2]); printf("# engineering db directory: %s\n",engdir); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='t') &&(strlen(argv[ac])>2) ) { strcpy(targetfile,&argv[ac][2]); if (findTarget(0,targetfile,NULL,NULL)==0) { printf("# WARNING can't access specified target file: %s\n",targetfile); } else { printf("# target specification file: %s\n",targetfile); } } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='h') &&(strlen(argv[ac])==2) ) { correct= 0; printf("# hack correction disabled\n"); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='?') &&(strlen(argv[ac])==2) ) { printHelpText(); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='p') &&(strlen(argv[ac])==2) ) { params= 1; printf("# extended parameter file reporting enabled\n"); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='l') &&(strlen(argv[ac])==2) ) { logging= 1; printf("# log file generation enabled\n"); } else if ( (argv[ac][0]=='-') &&(tolower(argv[ac][1])=='v') &&((strlen(argv[ac])==2)||(strlen(argv[ac])==3)) ) { if (strlen(argv[ac])==2) { verbose= 1; printf("# verbose on (1)\n"); } else if ((argv[ac][2]>='0')&&(argv[ac][2]<='9')) { verbose= argv[ac][2]-'0'; printf("# verbose set to %d\n",verbose); } } else { // get 'standard' file header an determine file type for all source files // create index files for hk and sci to allow for easy later sorting, // pre increment the file number so we don't use entry 0 strcpy(file[++files].realname,argv[ac]); if ((fp = fopen(file[files].realname,FMODEREAD))!=NULL) { if (readFileHeader(fp,&file[files])) { if (file[files].FileTypeID==SCIFILETYPEID) { // handle GSE case where the TypeId is set incorrectly to // science file for all files (even hk) now make the // distinction based on file extension ! if (strcmp(&file[files].realname[strlen(file[files].realname)-4],".sci")==0) { scifiles[strlen(scifiles)+1]='\0'; scifiles[strlen(scifiles)]= files; } else { printf("WARNING: file reclassified as hk although ID says sci: %s\n",argv[ac]); hkfiles[strlen(hkfiles)+1]='\0'; hkfiles[strlen(hkfiles)]= files; } } else if (file[files].FileTypeID==HKFILETYPEID) { hkfiles[strlen(hkfiles)+1]='\0'; hkfiles[strlen(hkfiles)]= files; } // Find the ApId of the CCSDS header in the first word of the 'standard' // header that was discarded from the definition and now an ugly check // has to be used, check for any of the SC ApIds delivered to LAMP else if ( (((file[files].FileTypeID>>16)&0x07ff)==LROTEMPAPID) ||(((file[files].FileTypeID>>16)&0x07ff)==LROPOWRAPID) ||(((file[files].FileTypeID>>16)&0x07ff)==LROSWIMAPID) ||(((file[files].FileTypeID>>16)&0x07ff)==LROSWIDAPID) ||(((file[files].FileTypeID>>16)&0x07ff)==LROPWRDAPID) ) { // SC housekeeping file found, extract the time from the CCSDS header // and insert it back in the 'standard' header location, only a start // time is needed to be able to sort the files. time.sec= ((file[files].Spare&0xffff)<<16)+((file[files].Start.sec>>16)&0xffff); time.sub= (unsigned short)(file[files].Start.sec&0xffff); file[files].Spare= 0; file[files].Start= time; file[files].Stop.sec= 0; file[files].Stop.sub= 0; scfiles[strlen(scfiles)+1]='\0'; scfiles[strlen(scfiles)]= files; } else { printf("ERROR: incorrect file id %3d in file %s\n",file[files].FileTypeID,argv[ac]); ok= 0; } } else { printf("ERROR: file too small (<64): %s\n",argv[ac]); ok= 0; } fclose(fp); } else { printf("ERROR: cannot access input file: %s\n",argv[ac]); ok= 0; } } } if ((files>0)&&(ok)) { // sort and process all input files sortFiles(scifiles,file); sortFiles(hkfiles,file); sortFiles(scfiles,file); if ((strlen(scifiles)>0)||(strlen(hkfiles)>0)) { ok= processLampFiles(verbose,correct,params,logging ,destdir,engdir,targetfile,scifiles,hkfiles,file); } if ((ok)&&(strlen(scfiles)>0)) { ok= processLroFiles(verbose,engdir,scfiles,file); } printf("# %.9s, processing done\n",VERSIONSTR); return(ok); } else { return(ok); } } else if (argc==1) { printf("usage: %s [-v[#]] [-l] [-p] [-h] [-c] [-d [-e]] [-t]\n", argv[0]); return(-1); } else { printf("ERROR: too many parameters (%d)\n",argc); return(-1); } } /* main() */ /*---- end of file ----------------------------------------------------------*/