This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DLPNIRSCANEVM: DLPNIRSCANEVM

Part Number: DLPNIRSCANEVM
Other Parts Discussed in Thread: DLPC350, ADS1255,

使用DLP的custom_scan功能进行测量时,得到的Spectrum Data和Raw ADC Data两组数据之间的联系是如何的呢?Raw ADC Data经过怎样的处理得到的Spectrum Data?我查阅使用手册,手册中说Spectrum Data是由平均之后的数值,原文是“Whereas the Download Spectrum button provides averaged reference and sample data values”,但我用Raw ADC Data进行平均得到的结果与Spectrum Data相差甚远。

  • 请提供测试步骤和结果。需要先测reference data,来去除本底数据。

  • 这是我按照手册步骤进行custom_scan测量得到的结果,其中第一张为Spectrum Data,第二张为Raw ADC Data。测得的Raw ADC Data数据共有209个pattern,同一pattern有41组数据,这个意思是对DMD的同一pattern进行了41次测量吗?图二中的Reference与sample数据是如何得到图一中reference和sample数据的呢?

  • 请问测试的是什么样品?

    1. 样品holder中不装样品,按run reference 扫描;先需要去除本底。

    2. 再加装样品,再按perform scan.

    得到结果

  • 我测量的是液态水的谱,我在测量时,在设定好测量参数后,放置了一个空holder先测量了reference,之后换的装有液态水的holder。另外我还测量了无水乙醇,得到的spectrum data和raw ADC data,但是还是不知道这两组数据的联系是如何的,请问这两组数据的联系是怎样的?raw ADC data是如何计算得到的spectrum data?Raw ADC Data是detector上测量,并经过ADC后的数据吗?

  • 从raw ADC data 转换到spectrum data比较复杂,不是简单的关系,需要积分曝光时间和强度的值。

    吸收率是: -1.0xlog(采样信号/参考样本信号) ,比如用户手册中: 0.0829 = -1.0x log(39560/47880)

    从raw ADC data 转换到spectrum data,请参考代码:

    NIRSCAN_NANO是在NIRSCAN之后推出的包含Hadamard优化了。

    static int convert_sanitize_readings(FILE *pReadingsFile, double *pReadings, int *pFrontNumReadingsToDiscard, int *pBackNumReadingsToDiscard, float threshold_factor)
    /**
    * After a scan is peformed using dlpspectro_scan_flash_images() or dlpspectro_stream_images() or dlpspectro_setup_calib_scan_prgb()
    * and mean_readings.txt is obtained, this function is used to discard the samples in the beginning or end of file that are below a certain
    * threshold compared to the median. This step is sometimes necessary before computing the absorption spectrum to discard the samples corresponding
    * to regions that may be outside of the illuminated region due to mechanical mis-alignment.
    *
    * @param pReadingsFile - I - Pointer to the file that contains the mean adc readings
    * @param pReadings - O - Pointer to the memory buffer in which the ADC readings read from the file shall be stored.
    * @param pFrontNumReadingsToDiscard - O - Number of samples found in the beginning of the file that are below the threshold and hence need to be discarded
    * @param pBackNumReadingsToDiscard - O - Number of samples found in the end of the file that are below the threshold and hence need to be discarded
    * @param threshold_factor - I - If threshold_factor is 0.001, all samples <= .001*median_value will be discarded
    *
    * @return 0 = SUCCESS
    * <0 = FAIL
    *
    */
    {
    char RdBuf[50];
    int numReadings=0;
    double *pSortedReadings;
    double median_val;
    int i;

    while(fscanf(pReadingsFile,"%s",RdBuf) != EOF)
    {
    *(pReadings + numReadings++) = strtod(RdBuf,NULL);
    }
    pSortedReadings = (double *)malloc(numReadings * sizeof(double));
    if(pSortedReadings == NULL)
    {
    DEBUG_ERR("ERROR: Unable to allocate memory\n");
    return -1;
    }

    memcpy(pSortedReadings, pReadings, numReadings*sizeof(double));
    qsort(pSortedReadings, numReadings, sizeof(double), compare_double);

    #if 1
    //median value is the mid point of sorted array
    if(numReadings & 1) //odd number of elements in the array
    median_val = pSortedReadings[numReadings/2];
    else
    median_val = pSortedReadings[numReadings/2-1]/2 + pSortedReadings[numReadings/2]/2;
    #else
    median_val = pSortedReadings[numReadings-1]/2;
    #endif

    DEBUG_MSG("In the readings file - Min value = %f, Max value = %f and Median value = %f\n", pSortedReadings[0], pSortedReadings[numReadings-1], median_val);

    //Traverse the readings and discard values below the threshold at the beginning and end of the array
    *pFrontNumReadingsToDiscard = 0;
    *pBackNumReadingsToDiscard = 0;

    for(i=0; i<numReadings; i++)
    {
    if(i < numReadings/2) //first half of the data
    {
    if(pReadings[i] <= median_val*threshold_factor)
    *pFrontNumReadingsToDiscard = i+1;
    }
    else
    {
    if(pReadings[i] <= median_val*threshold_factor)
    {
    *pBackNumReadingsToDiscard = numReadings-i;
    break;
    }
    }
    }

    free(pSortedReadings);
    }

    /*
    * This function generates the absorption spectrum
    */

    int dlpspectro_compute_absorption_spectrum(FILE *calRefSpectFilePtr, FILE *inputSampleSpectFilePtr, FILE *abspSpeFilePtr, float threshold_factor)
    /**
    * After a scan is peformed using dlpspectro_scan_flash_images() or dlpspectro_stream_images() or dlpspectro_setup_calib_scan_prgb()
    * and mean_readings.txt is obtained, this function is used to compute the absorption spectrum computed using one reference and one sample set of readings.
    *
    * @param calRefSpectFilePtr - I - Pointer to the file that contains the mean adc readings obtained from scanning the reference object.
    * @param inputSampleSpectFilePtr - I - Pointer to the file that contains the mean adc readings obtained from scanning the sample object.
    * @param abspSpeFilePtr - O - Pointer to the file that will contain the computed absorption spectrum from the above two inputs.
    * @param threshold_factor - I - If threshold_factor is 0.001, all samples <= .001*median_value will be discarded
    *
    * @return 0 = SUCCESS
    * <0 = FAIL
    *
    */
    {
    int i;
    double tempDoubleVal;
    int frontNumReadingsToDiscardSample;
    int backNumReadingsToDiscardSample;
    int frontNumReadingsToDiscardRef;
    int backNumReadingsToDiscardRef;
    int numReadings;
    double *pRefReadings;
    double *pSampleReadings;
    int ret = 0;

    //Find if number of lines in each file matching
    if((numReadings = find_num_lines_in_file(calRefSpectFilePtr)) != find_num_lines_in_file(inputSampleSpectFilePtr) )
    {
    DEBUG_ERR("ERROR: Mismatch in number of lines in reference and sample reading files\n");
    return -1;
    }

    pRefReadings = (double *)malloc(numReadings * sizeof(double));
    pSampleReadings = (double *)malloc(numReadings * sizeof(double));

    if((pRefReadings == NULL) || (pSampleReadings == NULL))
    {
    DEBUG_ERR("ERROR: Unable to allocate memory\n");
    return -1;
    }

    convert_sanitize_readings(calRefSpectFilePtr, pRefReadings, &frontNumReadingsToDiscardRef, &backNumReadingsToDiscardRef, threshold_factor);
    convert_sanitize_readings(inputSampleSpectFilePtr, pSampleReadings, &frontNumReadingsToDiscardSample, &backNumReadingsToDiscardSample, threshold_factor);

    // Setting discarded samples for sample measurement to the same as ref measurement, since ref is a better judge of where the spectrum begins and ends.
    frontNumReadingsToDiscardSample = frontNumReadingsToDiscardRef;
    backNumReadingsToDiscardSample = backNumReadingsToDiscardRef;

    if(frontNumReadingsToDiscardRef)
    {
    DEBUG_WRN("%d samples at the beginning of reference readings file discarded as noise\n", frontNumReadingsToDiscardRef);
    ret = frontNumReadingsToDiscardRef;
    }
    if(backNumReadingsToDiscardRef)
    {
    DEBUG_WRN("%d samples at the end of reference readings file discarded as noise\n", backNumReadingsToDiscardRef);
    ret = backNumReadingsToDiscardRef;
    }
    if(frontNumReadingsToDiscardSample)
    {
    DEBUG_WRN("%d samples at the beginning of sample readings file discarded as noise\n", frontNumReadingsToDiscardSample);
    ret = frontNumReadingsToDiscardSample;
    }
    if(backNumReadingsToDiscardSample)
    {
    DEBUG_WRN("%d samples at the end of sample readings file discarded as noise\n", backNumReadingsToDiscardSample);
    ret = backNumReadingsToDiscardSample;
    }

    if((frontNumReadingsToDiscardRef != frontNumReadingsToDiscardSample) || (backNumReadingsToDiscardRef != backNumReadingsToDiscardSample))
    {
    DEBUG_ERR("Mismatch in number of readings from reference and sample after discarding noise\n");
    free(pRefReadings);
    free(pSampleReadings);
    return -1;
    }

    for(i=0; i<numReadings-backNumReadingsToDiscardRef; i++)
    {
    if(i<frontNumReadingsToDiscardRef)
    {
    tempDoubleVal = COMPUTE_ABSORPTION_VAL(*(pSampleReadings+frontNumReadingsToDiscardRef), *(pRefReadings+frontNumReadingsToDiscardSample));
    }
    else
    {
    tempDoubleVal = COMPUTE_ABSORPTION_VAL(*(pSampleReadings+i), *(pRefReadings+i));
    }
    if(isnan(tempDoubleVal) || isinf(tempDoubleVal) || (tempDoubleVal <= 0))
    {
    DEBUG_WRN("ERROR: Invalid absorption value computed at reading #%d.. ref=%f, sample=%f, abs = %f\n", i, *(pRefReadings+i), *(pSampleReadings+i), tempDoubleVal);
    ret = 1;
    }
    fprintf(abspSpeFilePtr,"%f\n",tempDoubleVal);

    }

    free(pRefReadings);
    free(pSampleReadings);
    return ret;
    }

    static int dlpspectro_setup_scan_prgb(unsigned int exp_time_us)
    /**
    * This function configures DLPC350 controller to operate in pattern mode to display patterns coming in via
    * the parallel RGB interface. Inserts a black pattern at the beginning of every frame.
    *
    * @param exp_time_us - I - exposure time in microseconds for each pattern.
    *
    * @return 0 = SUCCESS
    * <0 = FAIL
    *
    */
    {
    int i;
    unsigned int status;
    int pat_reorder_lut[] = {24, 21, 18, 16, 8, 9, 10, 11, 12, 22, 19, 13, 14, 15, 0, 1, 2, 23, 20, 17, 3, 4, 5, 6, 7 };
    int pats_per_frame = (VSYNC_PERIOD_US - MIN_EXPOSURE_TIME_US)/exp_time_us; //subtract MIN_EXPOSURE_TIME_US because insertBlack requires that much time
    int trigger_type;
    bool bufSwap;
    bool insertBlack;
    int patNum;

    //Make the TRIG_OUT1 pulse as wide as possible so that the Sitara can "see" it
    // so give max -ve delay to the falling edge and max +ve delay to rising edge to make it as wide as possible
    LCR_SetTrigOutConfig(1, 0, 0xD5, 0);

    LCR_SetExposure_FramePeriod(exp_time_us, exp_time_us);
    LCR_SetPatternDisplayMode(true);//Patterns streamed via parallel rgb interface
    LCR_SetPatternTriggerMode(0); //VSYNC triggers the pattern display sequence
    LCR_SetPatternConfig(25, true, 25, 1); //25 entries in LUT, repeat LUT, TrigOut2 frames 25 patterns

    //Create the pattern LUT, send it and validate
    LCR_ClearPatLut();
    for(i=0; i<25; i++)
    {
    patNum = pat_reorder_lut[i];
    trigger_type = NO_TRIG;
    bufSwap = false;
    insertBlack = false;

    if(i==0) //First entry
    {
    bufSwap = true;
    trigger_type = EXT_POS;
    }
    else if (((i%pats_per_frame) == 0) && (use_alternate_streaming_method))
    {
    bufSwap = true;
    trigger_type = EXT_POS;
    }

    if(i==24)
    insertBlack = true;
    else if ((((i+1)%pats_per_frame) == 0) && (use_alternate_streaming_method)) //last sequence for this frame
    insertBlack = true;

    LCR_AddToPatLut(trigger_type, patNum, 1, 1, false, insertBlack, bufSwap, false);
    }

    LCR_SendPatLut();
    LCR_ValidatePatLutData(&status);
    if(status != 0)
    DEBUG_ERR("ValidatePatLutData returned 0x%x as status\n", status);

    return 0;
    }


    int dlpspectro_perform_scan(int spi_fd, int num_samples, int max_patterns, int ptn_exposure_time_us, FILE *raw_output_fd, FILE *mean_output_fd)
    /**
    * This function collects num_samples from the ADC, using the delimiter finds the average ADC reading per pattern and records
    * the mean as well as raw values in the files specified.
    *
    * @param spi_fd - I - SPI device handle to communicate with the ADC
    * @param num_samples - I - Total number of samples to be read from the ADC
    * @param max_patterns - I - If the samples read correspond to more number of patterns than max_patterns, discard those
    * @param ptn_exposure_time_us - I - exposure time of each pattern in microseconds
    * @param raw_output_fd - I - File pointer to record the raw readings in
    * @param mean_output_fd - I - File pointer to record the mean values in
    *
    * @return 0 = SUCCESS
    * <0 = FAIL
    *
    */
    {
    int32_t txrx[MAX_ADC_READ_SAMPLES] = {0, };
    char adc_readings[64];
    long long accumulator=0;
    int accu_start_idx;
    int accu_stop_idx;
    int num_patterns=0;
    int i,j;
    int loop_cnt;
    bool ptn_start_detected;
    int samples_per_pattern;
    int mean_black_level = 0;
    unsigned int num_black_patterns = 0;

    if(num_samples > MAX_ADC_READ_SAMPLES)
    {
    DEBUG_ERR("Max samples that can be read from the ADC is %d in this implementation\n", MAX_ADC_READ_SAMPLES);
    return -1;
    }
    samples_per_pattern = DEFAULT_ADC_SAMPLE_RATE_KSPS*ptn_exposure_time_us/1000;

    ads1255_write_command(spi_fd, ADS1255_RDATAC);

    ads1255_read_data(spi_fd, txrx, num_samples);
    ads1255_write_command(spi_fd, ADS1255_SDATAC);

    if(raw_output_fd != NULL)
    {
    for (i = 0; i < num_samples; i++)
    {
    sprintf(adc_readings, "0x%.8X\n", txrx[i]);
    fputs(adc_readings, raw_output_fd);
    }
    }
    ptn_start_detected = false;
    accu_start_idx = 0;
    for (i = 0; i < num_samples; i++)
    {
    //Skip readings until the first trigger out1 HIGH
    if(ptn_start_detected == false)
    {
    if((txrx[i] & 2) == 2)
    {
    ptn_start_detected = true;
    accu_start_idx = i+ DISCARD_WINDOW_FRONT;
    }
    }
    else if((txrx[i] & 2) == 0)//Check if BIT1 is 0
    {
    ptn_start_detected = false;
    accu_stop_idx = i - DISCARD_WINDOW_BACK;
    /* This while loop is to take care of cases when we miss to detect the TRIG OUT1 LOW period */
    loop_cnt=0;
    while(accu_start_idx < i)
    {
    if(accu_stop_idx > accu_start_idx)
    {
    if((accu_stop_idx - accu_start_idx) > samples_per_pattern)
    {
    DEBUG_ERR("TrigOUT1 LOW missed for %d samples after pattern %d\n", accu_stop_idx - accu_start_idx, num_patterns);
    accu_stop_idx = accu_start_idx + samples_per_pattern - (DISCARD_WINDOW_BACK+DISCARD_WINDOW_FRONT);
    }

    for(j=accu_start_idx; j < accu_stop_idx; j++)
    accumulator += txrx[j]>>8;
    accumulator /= (accu_stop_idx - accu_start_idx);
    txrx[num_patterns++] = accumulator;
    accumulator = 0;
    }
    else
    DEBUG_ERR("start > stop detected at sample #%d pattern #%d\n", i, num_patterns);

    accu_start_idx = accu_stop_idx + (DISCARD_WINDOW_FRONT+DISCARD_WINDOW_BACK);
    accu_stop_idx += samples_per_pattern;
    loop_cnt++;
    }
    if(loop_cnt > 1)
    DEBUG_ERR("Loop Count = %d\n", loop_cnt);
    }
    }

    #if 1
    //Ignore extra patterns found; process only as many as the user wants
    // Extra patterns are found because of the margin addded to num_samples and also because of
    // timing differnece between ADC read start and the first vsync (in streaming mode)
    num_patterns = MIN(num_patterns, max_patterns);

    /*Every 25th pattern is a black pattern - That value needs to be used to compute average black level and
    that pedestal needs to be subtracted from every other readings */
    for (i = 0; i < num_patterns; i+=25)
    {
    mean_black_level += txrx[i];
    num_black_patterns++;
    }
    mean_black_level /= num_black_patterns;
    DEBUG_MSG("Mean black level = %d\n", mean_black_level);

    for (i = 0, j=0; i < num_patterns; i++)
    {
    if(i%25 != 0)
    {
    txrx[j] = txrx[i] - mean_black_level;
    j++;
    }
    }
    num_patterns = j;
    #endif

    if(mean_output_fd != NULL)
    {
    for (i = 0; i < num_patterns; i++)
    {
    sprintf(adc_readings, "0x%.8X\n", txrx[i]);
    fputs(adc_readings, mean_output_fd);
    }
    }

    return 0;
    }

    static int convert_sanitize_readings(FILE *pReadingsFile, double *pReadings, int *pFrontNumReadingsToDiscard, int *pBackNumReadingsToDiscard, float threshold_factor)
    /**
     * After a scan is peformed using dlpspectro_scan_flash_images() or dlpspectro_stream_images() or dlpspectro_setup_calib_scan_prgb()
     * and mean_readings.txt is obtained, this function is used to discard the samples in the beginning or end of file that are below a certain
     * threshold compared to the median. This step is sometimes necessary before computing the absorption spectrum to discard the samples corresponding
     * to regions that may be outside of the illuminated region due to mechanical mis-alignment.
     *
     * @param   pReadingsFile - I - Pointer to the file that contains the mean adc readings
     * @param   pReadings - O - Pointer to the memory buffer in which the ADC readings read from the file shall be stored.
     * @param   pFrontNumReadingsToDiscard - O - Number of samples found in the beginning of the file that are below the threshold and hence need to be discarded
     * @param   pBackNumReadingsToDiscard - O - Number of samples found in the end of the file that are below the threshold and hence need to be discarded
     * @param   threshold_factor - I - If threshold_factor is 0.001, all samples <= .001*median_value will be discarded
     *
     * @return  0 = SUCCESS
     *          <0 = FAIL
     *
     */
    {
    	char RdBuf[50];
        int numReadings=0;
        double *pSortedReadings;
        double median_val;
        int i;
    
        while(fscanf(pReadingsFile,"%s",RdBuf) != EOF) 
        {
            *(pReadings + numReadings++) = strtod(RdBuf,NULL);
        }
        pSortedReadings = (double *)malloc(numReadings * sizeof(double));
        if(pSortedReadings == NULL)
        {
            DEBUG_ERR("ERROR: Unable to allocate memory\n");
            return -1;
        }
    
        memcpy(pSortedReadings, pReadings, numReadings*sizeof(double));
        qsort(pSortedReadings, numReadings, sizeof(double), compare_double);
    
    #if 1
        //median value is the mid point of sorted array
        if(numReadings & 1) //odd number of elements in the array
            median_val = pSortedReadings[numReadings/2];
        else
            median_val = pSortedReadings[numReadings/2-1]/2 + pSortedReadings[numReadings/2]/2;
    #else
            median_val = pSortedReadings[numReadings-1]/2;
    #endif
    
        DEBUG_MSG("In the readings file - Min value = %f, Max value = %f and Median value = %f\n", pSortedReadings[0], pSortedReadings[numReadings-1], median_val);
    
        //Traverse the readings and discard values below the threshold at the beginning and end of the array
        *pFrontNumReadingsToDiscard = 0;
        *pBackNumReadingsToDiscard = 0;
    
        for(i=0; i<numReadings; i++)
        {
            if(i < numReadings/2) //first half of the data
            {
                if(pReadings[i] <= median_val*threshold_factor)
                    *pFrontNumReadingsToDiscard = i+1;
            }
            else
            {
                if(pReadings[i] <= median_val*threshold_factor)
                {
                    *pBackNumReadingsToDiscard = numReadings-i;
                    break;
                }
            }
        }
        
        free(pSortedReadings);
    }
    
    /*
    * This function generates the absorption spectrum
    */
    
    int dlpspectro_compute_absorption_spectrum(FILE *calRefSpectFilePtr, FILE *inputSampleSpectFilePtr, FILE *abspSpeFilePtr, float threshold_factor)
    /**
     * After a scan is peformed using dlpspectro_scan_flash_images() or dlpspectro_stream_images() or dlpspectro_setup_calib_scan_prgb()
     * and mean_readings.txt is obtained, this function is used to compute the absorption spectrum computed using one reference and one sample set of readings.
     *
     * @param   calRefSpectFilePtr - I - Pointer to the file that contains the mean adc readings obtained from scanning the reference object.
     * @param   inputSampleSpectFilePtr - I - Pointer to the file that contains the mean adc readings obtained from scanning the sample object.
     * @param   abspSpeFilePtr - O - Pointer to the file that will contain the computed absorption spectrum from the above two inputs.
     * @param   threshold_factor - I - If threshold_factor is 0.001, all samples <= .001*median_value will be discarded
     *
     * @return  0 = SUCCESS
     *          <0 = FAIL
     *
     */
    {
    	int i;
    	double tempDoubleVal;
        int frontNumReadingsToDiscardSample;
        int backNumReadingsToDiscardSample;
        int frontNumReadingsToDiscardRef;
        int backNumReadingsToDiscardRef;
        int numReadings;
        double *pRefReadings;
        double *pSampleReadings;
        int ret = 0;
    
    	//Find if number of lines in each file matching
    	if((numReadings = find_num_lines_in_file(calRefSpectFilePtr)) != find_num_lines_in_file(inputSampleSpectFilePtr) )
    	{
    		DEBUG_ERR("ERROR: Mismatch in number of lines in reference and sample reading files\n");
    		return -1;
    	}
    
        pRefReadings = (double *)malloc(numReadings * sizeof(double));
        pSampleReadings = (double *)malloc(numReadings * sizeof(double));
    
        if((pRefReadings == NULL) || (pSampleReadings == NULL))
        {
            DEBUG_ERR("ERROR: Unable to allocate memory\n");
            return -1;
        }
    
        convert_sanitize_readings(calRefSpectFilePtr, pRefReadings, &frontNumReadingsToDiscardRef, &backNumReadingsToDiscardRef, threshold_factor);
        convert_sanitize_readings(inputSampleSpectFilePtr, pSampleReadings, &frontNumReadingsToDiscardSample, &backNumReadingsToDiscardSample, threshold_factor);
    
    	// Setting discarded samples for sample measurement to the same as ref measurement, since ref is a better judge of where the spectrum begins and ends.
    	frontNumReadingsToDiscardSample = frontNumReadingsToDiscardRef;
    	backNumReadingsToDiscardSample = backNumReadingsToDiscardRef;
    
        if(frontNumReadingsToDiscardRef)
        {
            DEBUG_WRN("%d samples at the beginning of reference readings file discarded as noise\n", frontNumReadingsToDiscardRef);
            ret = frontNumReadingsToDiscardRef;
        }
        if(backNumReadingsToDiscardRef)
        {
            DEBUG_WRN("%d samples at the end of reference readings file discarded as noise\n", backNumReadingsToDiscardRef);
            ret = backNumReadingsToDiscardRef;
        }
        if(frontNumReadingsToDiscardSample)
        {
            DEBUG_WRN("%d samples at the beginning of sample readings file discarded as noise\n", frontNumReadingsToDiscardSample);
            ret = frontNumReadingsToDiscardSample;
        }
        if(backNumReadingsToDiscardSample)
        {
            DEBUG_WRN("%d samples at the end of sample readings file discarded as noise\n", backNumReadingsToDiscardSample);
            ret = backNumReadingsToDiscardSample;
        }
    
        if((frontNumReadingsToDiscardRef != frontNumReadingsToDiscardSample) || (backNumReadingsToDiscardRef != backNumReadingsToDiscardSample))
        {
            DEBUG_ERR("Mismatch in number of readings from reference and sample after discarding noise\n");
            free(pRefReadings);
            free(pSampleReadings);
            return -1;
        }
        
        for(i=0; i<numReadings-backNumReadingsToDiscardRef; i++)
    	{
    		if(i<frontNumReadingsToDiscardRef)
    		{
    			tempDoubleVal = COMPUTE_ABSORPTION_VAL(*(pSampleReadings+frontNumReadingsToDiscardRef), *(pRefReadings+frontNumReadingsToDiscardSample));
    		}
    		else
    		{
    			tempDoubleVal = COMPUTE_ABSORPTION_VAL(*(pSampleReadings+i), *(pRefReadings+i));
    		}
    		if(isnan(tempDoubleVal) || isinf(tempDoubleVal) || (tempDoubleVal <= 0))
            {
                DEBUG_WRN("ERROR: Invalid absorption value computed at reading #%d.. ref=%f, sample=%f, abs = %f\n", i, *(pRefReadings+i), *(pSampleReadings+i), tempDoubleVal);
                ret = 1;
            }
            fprintf(abspSpeFilePtr,"%f\n",tempDoubleVal);
    
    	}
    
        free(pRefReadings);
        free(pSampleReadings);
    	return ret;
    }
    
    static float dlpspectro_find_peaks3(float y1, float y2, float y3)
    /*
    FINDPEAK3 - parabolic interpolation of peak location given 3
    equally-spaced points
    Returns a relative delta-x value to be added to the x-value of y(2).
    i.e., xpeak = x(2) + findpeak(y) * (x(3) - x(2));
    */
    {
        return (0.5 * (y1 - y3) / (y1 - 2 * y2 + y3));
    }
    
    int dlpspectro_find_peaks(FILE *absorp_spect_file, float peak_sel_divisor, float *peaks, float *peak_inds)
    /**
     * Finds and retuns the peak values and the locations of the peak for a given set of values.
     *
     * @param   absorp_spect_file - I - Pointer to the file that contains the computed absorption spectrum values.
     * @param   peak_sel_divisor - I - This input will decide the criteria to select peaks. Values that are higher than the preceding dip/valley by
     *                                  an amount >= (max-min)/peak_sel_divisor will be counted as a peak.
     * @param   peaks - O - Peak values in the absorption spectrum file input that matches the condition above.
     * @param   peak_inds - O - Location/index/position of the Peak values in the absorption spectrum file input that matches the condition above.
     *
     * @return  number of peaks found in the input data set
     *          <0 = FAIL
     *
     */
    {
    	char line[MAX_CSV_LINE_LEN];
        float *values=NULL;
        float *diff_vals=NULL;
        float *peak_vals=NULL;
        char *endPtr; //used in strtod function
        int i;
        int j;
        int file_len;
        int *peak_valley_indices=NULL;
        int *peak_locs=NULL;
        int num_peaks_vallies;
        float *peaks_vallies=NULL;
        float min_val;
        float max_val;
        float sel;
        int ret_val=0;
        int start_index;
        float left_min;
        float temp_val;
        int temp_loc;
        int max_peaks;
        bool found_peak;
        int peak_index;
        float start_offset = 2.5;
    
        file_len = find_num_lines_in_file(absorp_spect_file);
        values = (float *)malloc(file_len*sizeof(float));
        diff_vals = (float *)malloc((file_len-1)*sizeof(float));
        peak_valley_indices = (int *)malloc(((file_len+1)/2+2) * sizeof(int));
        peaks_vallies = (float *)malloc(((file_len+1)/2+2)*sizeof(float));
        if(values == NULL || diff_vals == NULL || peaks_vallies == NULL || peak_valley_indices == NULL)
        {
            DEBUG_ERR("Unable to allocate memory\n");
            ret_val = -1;
            goto cleanup_and_exit;
        }
    
        // Read all values from file
        for(i=0; i<file_len; i++)
        {
            fgets(line, MAX_CSV_LINE_LEN, absorp_spect_file); // Read one line from file
            values[i] = strtof(line, &endPtr);
        }
    
        //Find derivatives or diffs 
        for(i=0; i < file_len-1; i++)
        {
            diff_vals[i] = values[i+1] - values[i];
        }
    
        //Find indices where derivate changes sign - these are our potential peaks and valleys
        j=0;
        /* Include end points in potential peaks and vallies */
        //disabling end points because last pattern (910) was commonly causing a peak
    	//peak_valley_indices[j++] = 0;
        num_peaks_vallies = 0;
        for(i=1; i<file_len-1; i++)
        {
            if(SIGN(diff_vals[i]) != SIGN(diff_vals[i-1]))
            {
                peak_valley_indices[j++] = i;
            }
        }
    
        /* Include end points in potential peaks and vallies */
    	//disabling end points because last pattern (910) was commonly causing a peak
    	//peak_valley_indices[j++] = file_len-1;
    	//num_peaks_vallies = j;
    	num_peaks_vallies = j-1;
        if(num_peaks_vallies <= 2)
        {
            DEBUG_ERR("The data does not have peaks/valleys\n");
            ret_val = -1;
            goto cleanup_and_exit;
        }
        min_val = values[0];
        max_val = values[0];
        for(i=0; i<num_peaks_vallies; i++)
        {
            peaks_vallies[i] = values[peak_valley_indices[i]];
            if(peaks_vallies[i] < min_val)
                min_val = peaks_vallies[i];
            if(peaks_vallies[i] > max_val)
                max_val = peaks_vallies[i];
        }
        sel = (max_val - min_val)/peak_sel_divisor;
        //DEBUG_MSG("min_val = %f, max_val = %f\n", min_val, max_val);
    
        /* Deal with first point - since we just took it, it may not alternate like the rest*/
        start_index=0;
        /* if first value is larger than second and second is larger than third, we can remove the second point from potential peaks */
        if( peaks_vallies[0] >= peaks_vallies[1] )
        {
            if(peaks_vallies[1] >= peaks_vallies[2])
            {
                peaks_vallies[1] = peaks_vallies[0];
                start_index = 1;
            }
        }
        else
        {
            /* if first value is smaller than second and second is smaller than third, we can remove the first two points from potential peaks */
            if( peaks_vallies[1] < peaks_vallies[2] )
                start_index = 2;
            else
                start_index = 1;
        }
    
        /* Initialize loop variables */
    #if 0
        DEBUG_MSG("num_peaks_vallies = %d\n", num_peaks_vallies);
        DEBUG_MSG("we are starting at the peak at index %d\n", start_index);
        DEBUG_MSG("Selectivity divisor = %d\n", peak_sel_divisor);
    #endif
        max_peaks = (num_peaks_vallies-start_index+1)/2;
        peak_locs = (int *)malloc(max_peaks*sizeof(int));
        peak_vals = (float *)malloc(max_peaks*sizeof(float));
        if(peak_locs == NULL || peak_vals == NULL)
        {
            DEBUG_ERR("Unable to allocate memory\n");
            ret_val = -1;
            goto cleanup_and_exit;
        }
        found_peak = false;
        temp_val = min_val;
        left_min = min_val;
        j=0;
    
        for(i=start_index; i<num_peaks_vallies-1; i++)
        {
            /* i is at a peak */
            /* Reset peak finding if we had a peak and next peak is larger than this or if the left min was small enough */
            if(found_peak)
            {
                temp_val = min_val;
                found_peak = false;
            }
    
            if((peaks_vallies[i] > temp_val) && (peaks_vallies[i] > left_min + sel))
            {
                temp_loc = i;
                temp_val = peaks_vallies[i];
            }
    
            i++; //Move on to the valley
            if(i == num_peaks_vallies-1)
                break; //Make sure we don't read past the array
    
            /* down at least sel from peak */
            if((found_peak == false) && (temp_val > peaks_vallies[i] + sel))
            {
                found_peak = true;
                left_min = peaks_vallies[i];
                peak_locs[j] = temp_loc; //Add peak to index
                peak_vals[j] = temp_val;
                j++;
            }
            else if(peaks_vallies[i] < left_min) //new left min
                left_min = peaks_vallies[i];
        }
    
        //Handle last point
        if((peaks_vallies[num_peaks_vallies-1] > temp_val) && (peaks_vallies[num_peaks_vallies-1] > left_min + sel))
        {
            peak_locs[j] = num_peaks_vallies-1; 
            peak_vals[j] = peaks_vallies[num_peaks_vallies-1];
            j++;
        }
        else if((found_peak == false) && (temp_val > min_val))
        {
            peak_locs[j] = temp_loc;
            peak_vals[j] = temp_val;
            j++;
        }
    
        for(i=0; i<j; i++)
        {
            peaks[i] = peak_vals[i];
            peak_index = peak_valley_indices[peak_locs[i]];
            //Do parabolic interpolation of the peaks
            peak_inds[i] = start_offset + 2*(peak_index + dlpspectro_find_peaks3(values[peak_index-1], values[peak_index], values[peak_index+1]));
        }
        ret_val = j;
    
    cleanup_and_exit:
        if(values != NULL)
            free(values);
        if(diff_vals != NULL)
            free(diff_vals);
        if(peak_valley_indices != NULL)
            free(peak_valley_indices);
        if(peaks_vallies != NULL)
            free(peaks_vallies);
    
        if(peak_locs != NULL)
            free(peak_locs);
        if(peak_vals != NULL)
            free(peak_vals);
        return ret_val;
    }
    
    int dlpspectro_polyfit(double *x_peaks, double *l_peaks, double*x_to_l_coeffs)
    /**
     * Finds a second order polynomial that fits the given x and y input values and returs the three co-efficients for that polynomial.
     *
     * @param   x_peaks - I - Pointer to the x values
     * @param   l_peaks - I - Pointer to the y values
     * @param   x_to_l_coeffs - O - Pointer to the computed polynomial coefficients in the following order c, b, a which should be used to compute any given y value as
     *                                       y = ax2 + bx + c
     *
     * @return  number of peaks found in the input data set
     *          <0 = FAIL
     *
     */
    {
        double x_trans[3][6],a[3][3],res[3][6], x[6][3];
        double a_inv[3][3];
        int i,j;
        double determinant=0;
    
        //Create X matrix
        for(i=0;i<6;i++){
            x[i][0]=1;
            x[i][1]=x_peaks[i];
            x[i][2]=x_peaks[i]*x_peaks[i];
        }
    
        //get x transpose
        matrix_transpose(&x[0][0],&x_trans[0][0],6,3);
    
        //x_trans * x
        matrix_mult(&x_trans[0][0],&x[0][0],&a[0][0],3,6,3);
    
    
        //Get inverse of (X_trans*X) matrix
        determinant = a[0][0]*((a[1][1]*a[2][2]) - (a[2][1]*a[1][2])) -a[0][1]*(a[1][0]*a[2][2] - a[2][0]*a[1][2]) + a[0][2]*(a[1][0]*a[2][1] - a[2][0]*a[1][1]);
        for(j=0;j<3;j++){
            for(i=0;i<3;i++)
                a_inv[i][j]= ((a[(i+1)%3][(j+1)%3] * a[(i+2)%3][(j+2)%3]) - (a[(i+1)%3][(j+2)%3]*a[(i+2)%3][(j+1)%3]))/ determinant;
        }
    
        matrix_mult(&a_inv[0][0], &x_trans[0][0], &res[0][0],3, 3, 6);
    
        //Get beta matrix
        matrix_mult(&res[0][0], l_peaks, x_to_l_coeffs,3,6,1);
    
        return 0;
    }
    
    int dlpspectro_add_images_to_firmware(char *fw_filename, int num_images)
    /**
     *  Adds/Replaces the images in given firmware image with images from scan_img_000.bmp thru scan_img_num_images.bmp
     *
     * @param   fw_filename  - I - Name/path to the firmware file to be modified.
     * @param   num_images   - I - Number of images to be added/replaced in the firmware file. Replaces images starting with the one at index 1
     *
     * @return   0 = SUCCESS
     *          <0 = FAIL
     *
     */
    {
        int file_size;
        unsigned int new_fw_size;
        unsigned char *pFw_file_contents;
        unsigned char *pNewFw_contents;
        int ret;
        char inBmpFileName[64];
        int i;
        int splash_idx;
        int start_splash_index = 1;
        FILE *out_fw_fd;
        FILE *tmp_bmp_fd;
        unsigned char *pImageBuffer = imageBuffer;
        unsigned char *pByteArray;
        unsigned char compression;
        unsigned int compSize;
        BMP_Image_t splashImage;
        int num_splash_in_fwimage;
    
        pFw_file_contents = read_file_into_mem(fw_filename, &file_size);
    
        if( (pFw_file_contents == NULL) || (num_images <= 0))
            return -1;
    
        ret = Frmw_CopyAndVerifyImage(pFw_file_contents, file_size);
        if(ret == ERROR_FRMW_FLASH_TABLE_SIGN_MISMATCH)
        {
            DEBUG_ERR("Flash table signature mismatch : Bad firmware image\n");
            goto cleanup_and_exit;
        }
        else if (ret == ERROR_NO_MEM_FOR_MALLOC)
        {
            DEBUG_ERR("Malloc failed\n");
            goto cleanup_and_exit;
        }
    
        num_splash_in_fwimage = Frmw_GetSplashCount();
        if(start_splash_index > num_splash_in_fwimage)
        {
            DEBUG_ERR("Incorrect start splash index specified %d\n", start_splash_index);
            goto cleanup_and_exit;
        }
        if(start_splash_index + num_images > num_splash_in_fwimage)
            num_splash_in_fwimage = start_splash_index + num_images;
    
        Frmw_SPLASH_InitBuffer(num_splash_in_fwimage);
    
        for(splash_idx = 0; splash_idx < Frmw_GetSplashCount(); )
        {
            if(splash_idx == start_splash_index)
            {
                for(i=0; i<num_images; i++)
                {
                    DEBUG_MSG("splash_index = %d replaced\n", splash_idx+i);
                    sprintf(inBmpFileName, "scan_img_%.3d.bmp", i);
                    pByteArray = read_file_into_mem(inBmpFileName, &file_size);
    
                    compression = SPLASH_NOCOMP_SPECIFIED;
                    ret = Frmw_SPLASH_AddSplash(pByteArray, &compression, &compSize);
                    if (ret < 0)
                    {
                        switch(ret)
                        {
                            case ERROR_NOT_BMP_FILE:
                                DEBUG_ERR("Error building firmware - %s not in BMP format\n", inBmpFileName);
                                break;
                            case ERROR_NOT_24bit_BMP_FILE:
                                DEBUG_ERR("Error building firmware - %s not in 24-bit format\n", inBmpFileName);
                                break;
                            case ERROR_NO_MEM_FOR_MALLOC:
                                DEBUG_ERR("Error building firmware with %s - Insufficient memory\n", inBmpFileName);
                                break;
                            default:
                                DEBUG_ERR("Error building firmware with %s - error code %d\n", inBmpFileName, ret);
                                break;
                        }
                        goto cleanup_and_exit;
                    }
    
                    free(pByteArray);
                }
                splash_idx += num_images;
            }
            //Get and put the same splash at splash_index 
            else // if(splash_index != start_splash_index
            {
                DEBUG_MSG("splash_index = %d\n", splash_idx);
                memset(pImageBuffer, 0, DMD_WIDTH*DMD_HEIGHT*BYTES_PER_PIXEL);
                Frmw_GetSpashImage(pImageBuffer, splash_idx);
    
                tmp_bmp_fd = fopen("tmp.bmp", "w+");
                if(tmp_bmp_fd == NULL)
                {
                    DEBUG_ERR("Unable to open tmp.bmp\n");
                    ret = -1;
                    break;
                }
    
                BMP_InitImage(&splashImage, DMD_WIDTH, DMD_HEIGHT, 8*BYTES_PER_PIXEL);
                ret = BMP_StoreImage(&splashImage,(BMP_DataFunc_t *)Write_to_File, tmp_bmp_fd, (BMP_PixelFunc_t *)image_get, NULL);
                if(ret != 0)
                {
                    DEBUG_ERR("BMP_StoreImage returned %d\n", ret);
                    break;
                }
                fclose(tmp_bmp_fd);
    
                pByteArray = read_file_into_mem("tmp.bmp", &file_size);
    
                compression = SPLASH_NOCOMP_SPECIFIED;
                ret = Frmw_SPLASH_AddSplash(pByteArray, &compression, &compSize);
                free(pByteArray);
                if (ret < 0)
                {
                    DEBUG_ERR("Frmw_SPLASH_AddSplash returned %d\n", ret);
                    break;
                }
    
                splash_idx++;
            }
        }
    
        if(ret >= 0)
        {
            Frmw_Get_NewFlashImage(&pNewFw_contents, &new_fw_size);
            if((pNewFw_contents == NULL) || (new_fw_size <= 0))
            {
                DEBUG_ERR("New firmware image invalid\n");
                ret = -1;
                goto cleanup_and_exit;
            }
            out_fw_fd = fopen("new_firmware.bin", "w+");
            fwrite(pNewFw_contents, 1, new_fw_size, out_fw_fd);
            fclose(out_fw_fd);
        }
    
    cleanup_and_exit:
        free(pFw_file_contents);
        return ret;
    }
    
    static char *trimwhitespace(char *str)
    /**
     * Helper function that trims the given character string of the whitespace at either end. 
     *
     * @param   str  - I - Pointer to input string.
     *
     * @return   pointer to the trimmed string
     *
     */
    {
        char *end;
    
        //Trim leading white spaces
        while(isspace(*str))
            str++;
    
        if(*str == 0)
            return str; //reached end of line
    
        //Trim trailing white spaces
        end = str + strlen(str) - 1;
        while((end > str) && (isspace(*end)))
            end--;
    
        *(end+1) = 0;//Terminate string here
    
        return str;
    }
    
    static bool process_flash_params_line(char *line)
    {
        unsigned int MfgID, DevID, i;
        char *pToken;
    
        line = trimwhitespace(line);
        if(line[0] == '/')
            return false;
    
        if(line[0] == 0) //if line is empty
            return false;
    
        strtok(line, ",");	//Get first token and discar - this is Mfg Name
    
        pToken = strtok(NULL, ","); //Get token #1
        if(*pToken == 0)
            return false;
    
        MfgID = strtol(trimwhitespace(pToken), NULL, 16);
        pToken = strtok(NULL, ","); //Get token #2 and discard - this is Device Name
        if(*pToken == 0)
            return false;
        pToken = strtok(NULL, ","); //Get token #3
        if(*pToken == 0)
            return false;
        DevID = strtol(trimwhitespace(pToken), NULL, 16);
    
        if((MfgID == myFlashDevice.Mfg_ID) && (DevID == myFlashDevice.Dev_ID))
        {
            pToken = strtok(NULL, ","); //Get token #4
            if(*pToken == 0)
                return false;
            myFlashDevice.Size_MBit = strtol(trimwhitespace(pToken), NULL, 10);
            pToken = strtok(NULL, ","); //Get token #5
            if(*pToken == 0)
                return false;
            myFlashDevice.Type = strtol(trimwhitespace(pToken), NULL, 10);
            pToken = strtok(NULL, ","); //Get token #6 and discard
            if(*pToken == 0)
                return false;
            pToken = strtok(NULL, ","); //Get token #7
            if(*pToken == 0)
                return false;
            myFlashDevice.numSectors = strtol(trimwhitespace(pToken), NULL, 10);
    
            for(i=0; i<myFlashDevice.numSectors; i++)
            {
                pToken = strtok(NULL, ","); //Get next token
                if(*pToken == 0)
                    return false;
                myFlashDevice.SectorArr[i] = strtol(trimwhitespace(pToken), NULL, 16);
            }
    
            return true;
        }
        return false;
    }
    
    static int GetSectorNum(unsigned int Addr)
    {
        unsigned int i;
        for(i=0; i < myFlashDevice.numSectors; i++)
        {
            if(myFlashDevice.SectorArr[i] > Addr)
                break;
        }
        return i-1;
    }
    
    int dlpspectro_update_firmware(char *fw_filename)
    {
        unsigned short manID;
        unsigned long long devID;
        int startSector=0, i, BLsize=0, lastSectorToErase;
        unsigned char *pByteArray=NULL;
        unsigned int dataLen, dataLen_full;
        int fw_file_size;
        int bytesSent;
        unsigned int expectedChecksum=0, checksum;
        long long percent_completion = 0;
        char line[MAX_FLASH_PARAM_LINE_LEN];
        FILE *flparam_fp;
        int ret;
    
        ret = USB_Init();
    
        if(USB_IsConnected())
            USB_Close();
    
        if(USB_Open() != 0)
            DEBUG_ERR("USB connection to DLPC350 failed\n");
        else if (USB_IsConnected())
            DEBUG_MSG("USB connection to DLPC350 now open\n");
    
        pByteArray = read_file_into_mem(fw_filename, &fw_file_size);
        if(pByteArray == NULL)
            return -1;
    
        if(LCR_EnterProgrammingMode() < 0)
        {
            DEBUG_ERR("Unable to enter Programming mode\n");
            ret = -1;
            goto cleanup_and_exit;
        }
    
        USB_Close();
        DEBUG_ERR("Waiting to enter programming mode\n");
    
        sleep(5); //Wait for 5 seconds
        DEBUG_ERR("Wait over to enter programming mode\n");
    
        if(USB_Open() != 0)
            DEBUG_ERR("USB connection to DLPC350 failed\n");
        else if (USB_IsConnected())
            DEBUG_MSG("USB connection to DLPC350 now open\n");
    
        if(LCR_GetFlashManID(&manID) < 0)
        {
            DEBUG_ERR("Unable to read Flash Manufacturer ID");
            ret = -1;
            goto cleanup_and_exit;
        }
        if(LCR_GetFlashDevID(&devID) < 0)
        {
            DEBUG_ERR("Unable to read Flash Device ID");
            ret = -1;
            goto cleanup_and_exit;
        }
        devID &= 0xFFFF;
    
        myFlashDevice.Mfg_ID = manID;
        myFlashDevice.Dev_ID = devID;
    
        flparam_fp = fopen("FlashDeviceParameters.txt", "r");
        if(flparam_fp == NULL)
        {
            DEBUG_ERR("Unable to open FlashDeviceParameters.txt");
            ret = -1;
            goto cleanup_and_exit;
        }
    
        bool found = false;
        while (!feof(flparam_fp))
        {
            fgets(line, MAX_FLASH_PARAM_LINE_LEN, flparam_fp);
            if(process_flash_params_line(line))
            {
                found = true;
                break;
            }
        }
    
        if(found == false)
        {
            DEBUG_ERR("Unsupported Flash Device : Manufacturer ID = 0x%x & Device ID = 0x%llx", manID, devID);
            ret = -1;
            goto cleanup_and_exit;
        }
    
        if(true) //Always skip bootloader 
        {
            BLsize = 128 * 1024;
        }
    
        startSector = GetSectorNum(BLsize);
        lastSectorToErase = GetSectorNum(fw_file_size);
        if(fw_file_size == myFlashDevice.SectorArr[lastSectorToErase]) //If perfectly aligned with last sector start addr, no need to erase last sector.
            lastSectorToErase -= 1;
    
        LCR_SetFlashType(myFlashDevice.Type);
        i=0;
        DEBUG_ERR("Erasing Flash Sectors %d", i);
        fflush(stdout);
    
        for(i=startSector; i <= lastSectorToErase; i++)
        {
            LCR_SetFlashAddr(myFlashDevice.SectorArr[i]);
            LCR_FlashSectorErase();
            LCR_WaitForFlashReady();    //Wait for flash busy flag to go off
            DEBUG_ERR("\b\b\b %d", (i*100/lastSectorToErase));
            fflush(stdout);
        }
    
        DEBUG_ERR("\nErasing Flash Sectors Complete\n");
        dataLen = fw_file_size;
        dataLen -= BLsize;
    
        LCR_SetFlashAddr(BLsize);
        LCR_SetDownloadSize(dataLen);
    
        dataLen_full = dataLen;
        i=0;
        DEBUG_ERR("Downloading Firmware Image %d", i);
        fflush(stdout);
    
        while(dataLen > 0)
        {
            bytesSent = LCR_DownloadData(pByteArray+BLsize+dataLen_full-dataLen, dataLen);
    
            if(bytesSent < 0)
            {
                DEBUG_ERR("Flash Data Download Failed");
                ret = -1;
                goto cleanup_and_exit;
            }
            for(i=0; i<bytesSent; i++)
            {
                expectedChecksum += pByteArray[BLsize+dataLen_full-dataLen+i];
            }
    
            dataLen -= bytesSent;
            if(percent_completion != (((dataLen_full-dataLen)*100)/dataLen_full))
            {
                percent_completion = (((dataLen_full-dataLen)*100)/dataLen_full);
                DEBUG_ERR(" \b\b\b%d", (int)percent_completion);
                fflush(stdout);
            }
        }
        DEBUG_ERR("Waiting for checksum verification");
        LCR_CalculateFlashChecksum();
    
        LCR_WaitForFlashReady();
    
        if(LCR_GetFlashChecksum(&checksum) < 0)
        {
            DEBUG_ERR("Error reading checksum from target");
        }
        else  if(checksum != expectedChecksum)
        {
            DEBUG_ERR("Checksum mismatch: Expected %x; Received %x", expectedChecksum, checksum);
        }
        else
        {
            LCR_ExitProgrammingMode(); //Exit programming mode; Start application.
            DEBUG_ERR("Download Complete");
        }
    
    cleanup_and_exit:
        USB_Close();https://e2echina.ti.com/tinymce//apis/embeddables/configure?typeId=dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2&id=undefined#
        free(pByteArray);
        return ret;
    
    }
    

    源代码实现ADC sample和光谱的计算方法。

    https://www.ti.com/lit/zip/tidcc47
    www.ti.com/.../tidcc49

  • DLPNIRSCANEVM是否有像DLPNIRSCANNano的GUI一样的控制软件?进行DMD的设置操作,例如对DMD的设置不同的pattern,每个pattern有不同的曝光时间。

  • 这是两个不同产品,TI提供的GUI不一样。