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.

SpO2 calculation in MSP430fg439

Other Parts Discussed in Thread: MSP430FG439, MSP430FG437

hi sir

i have problem that in msp430fg439 having application which is in the medical portion pulse oximeter in that i found one difficulty is that in the programming SpO2 look up table offset and Ratio offset what is the difference how they make it from which reference they have decided it please sir help me 

i am going to mention the program where they have done it .

/* SaO2 Computation */
        x = log_sq_ir_heart_ac_signal/log_heart_signal_sample_counter;
        y = log_sq_vs_heart_ac_signal/log_heart_signal_sample_counter;
        Ratio = (unsigned int) (100.0*log(y)/log(x));
        if (Ratio > 66)
          SaO2 = Lookup[Ratio - 66];        // Ratio - 50 (Look-up Table Offset) - 16 (Ratio offset)   ///  my question over here only how they have decided ???
        else if (Ratio > 50)
          SaO2 = Lookup[Ratio - 50];        // Ratio - 50 (Look-up Table Offset)
        else
          //SaO2 = 100;
          SaO2 = 99;

and sir i show the look up table also so u can able to judge  it

// SaO2 Look-up Table
const unsigned int Lookup [43] = {100,100,100,100,99,99,99,99,99,99,98,98,98,98,
                                  98,97,97,97,97,97,97,96,96,96,96,96,96,95,95,
                                  95,95,95,95,94,94,94,94,94,93,93,93,93,93};

and also i show the my look up table which i have design for my application because above one will not show the lower SpO2 % thatswhy ..i show my look up table

const unsigned int Lookup [160] ={     

                                  99,99,99,99,99,98,98,98,98,98,97,97,97,97,97,96,96,96,96,96,95,95,95,94,94,94,
                                  93,93,93,92,92,92,91,91,91,91,90,90,90,90,89,89,89,89,88,88,88,88,87,87,87,87,
                                  86,86,86,86,85,85,85,85,84,84,84,84,83,83,83,83,82,82,82,82,81,81,81,81,80,80,
                                  80,80,79,79,79,79,78,78,78,78,77,77,77,77,76,76,76,76,75,75,75,75,74,74,74,73,
                                  73,73,72,72,72,71,71,71,70,70,70,69,69,69,68,68,67,67,67,67,66,66,66,65,65,65,
                                  64,64,64,63,63,63,62,62,62,62,61,61,61,61,60,60,60,59,59,59,58,58,58,57,57,57,
                                  56,56,56,56
                                  };              

in my case i have to change that value otherwise i am not able to get the accurate result, so please give answer with proper explanation that how should calculate the ratio offset and look up table offset ..

  • I guess your question is more into mathematics and medical than programming..

  • I have met same questions, dc offest been killed ,how to calculate sao2??

    I am not sure that what theory his calculation method based on.
  • there is nobody is there who can give me the answer ....................................

    and also i am asking the question for programming just asking that TI people have done but how it is coming please explain to me

    in SpO2 calculation what is the Ratio offset ?? look up table offset i knew but how to calculate the Ratio offset  please explain  to me ...........

  • but i hope u may try o answer my question so please explain me that how to calculate the ratio offset and look up table offset ...without it i can't give any justification for that please help me out 

  • A google search with SaO2 calculus???
     Oxigenation on wikipedia explaining some of the underlying theory...

     Pulse Oxymetry again on wikipedia.

     We don't know nothing about what are you doing so none can help you.

     Your code has little or no sense due to table log ratio.

  • Let me put full code then if anybody can answer of meaning of the ratio offset

    full code is

    //*****************************************************************************
    // THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR
    // REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,
    // INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
    // FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR
    // COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE.
    // TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET
    // POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY
    // INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR
    // YOUR USE OF THE PROGRAM.
    //
    // IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
    // CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY
    // THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED
    // OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT
    // OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM.
    // EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF
    // REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS
    // OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF
    // USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S
    // AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF
    // YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS
    // (U.S.$500).
    //
    // Unless otherwise stated, the Program written and copyrighted
    // by Texas Instruments is distributed as "freeware".  You may,
    // only under TI's copyright in the Program, use and modify the
    // Program without any charge or restriction.  You may
    // distribute to third parties, provided that you transfer a
    // copy of this license to the third party and the third party
    // agrees to these terms by its first use of the Program. You
    // must reproduce the copyright notice and any other legend of
    // ownership on each copy or partial copy, of the Program.
    //
    // You acknowledge and agree that the Program contains
    // copyrighted material, trade secrets and other TI proprietary
    // information and is protected by copyright laws,
    // international copyright treaties, and trade secret laws, as
    // well as other intellectual property laws.  To protect TI's
    // rights in the Program, you agree not to decompile, reverse
    // engineer, disassemble or otherwise translate any object code
    // versions of the Program to a human-readable form.  You agree
    // that in no event will you alter, remove or destroy any
    // copyright notice included in the Program.  TI reserves all
    // rights not specifically granted under this license. Except
    // as specifically provided herein, nothing in this agreement
    // shall be construed as conferring by implication, estoppel,
    // or otherwise, upon you, any license or other right under any
    // TI patents, copyrights or trade secrets.
    //
    // You may not use the Program in non-TI devices.
    //*****************************************************************************
    //    MSP430FG437 based pulse oximeter demonstration - Version II
    //    V. Chan and S. Underwood
    //    May 2005
    //    Modified by Bhargavi Nisarga
    //    April 2008
    //    All modifications related to Olimex's LCD were made by
    //    Penko T. Bozhkov, Olimex LTD
    //    June 2011
    //*****************************************************************************
    #include  <msp430xG43x.h>
    #include "stdint.h"
    #include "intrinsics.h"
    #include "math.h"

    // LCD Segment Configuration
    #define seg_a       0x01
    #define seg_b       0x02
    #define seg_c       0x04
    #define seg_d       0x08
    #define seg_e       0x40
    #define seg_f       0x10
    #define seg_g       0x20
    #define seg_h       0x80

    #define NUM_0   (seg_a | seg_b | seg_c | seg_d | seg_e | seg_f)
    #define NUM_1   (seg_b | seg_c)
    #define NUM_2   (seg_a | seg_b | seg_d | seg_e | seg_g)
    #define NUM_3   (seg_a | seg_b | seg_c | seg_d | seg_g)
    #define NUM_4   (seg_b | seg_c | seg_f | seg_g)
    #define NUM_5   (seg_a | seg_c | seg_d | seg_f | seg_g)
    #define NUM_6   (seg_a | seg_c | seg_d | seg_e | seg_f | seg_g)
    #define NUM_7   (seg_a | seg_b | seg_c)
    #define NUM_8   (seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g)
    #define NUM_9   (seg_a | seg_b | seg_c | seg_d | seg_f | seg_g)
    #define NUM_A   (seg_a | seg_b | seg_c | seg_e | seg_f | seg_g)
    #define NUM_B   (seg_c | seg_d | seg_e | seg_f | seg_g)
    #define NUM_C   (seg_a | seg_d | seg_e | seg_f)
    #define NUM_D   (seg_b | seg_c | seg_d | seg_e | seg_g)
    #define NUM_E   (seg_a | seg_d | seg_e | seg_f | seg_g)
    #define NUM_F   (seg_a | seg_e | seg_f | seg_g)


    // *****************************************************************
    // Definitions related to Olimex's LCD Digits and initialization!!!!
    // *****************************************************************
    // Definitions for Olimex LCD digits 10 and 11
    #define a 0x10
    #define b 0x01
    #define c 0x04
    #define d 0x08
    #define e 0x40
    #define f 0x20
    #define g 0x02
    #define h 0x80
    // Character generator definition for display digits 10 and 11
    const char char_gen_10_11[] = {
        a+b+c+d+e+f,                            // 0 Displays "0"
        b+c,                                    // 1 Displays "1"
        a+b+d+e+g,                              // 2 Displays "2"
        a+b+c+d+g,                              // 3 Displays "3"
        b+c+f+g,                                // 4 Displays "4"
        a+c+d+f+g,                              // 5 Displays "5"
        a+c+d+e+f+g,                            // 6 Displays "6"
        a+b+c,                                  // 7 Displays "7"
        a+b+c+d+e+f+g,                          // 8 Displays "8"
        a+b+c+d+f+g,                            // 9 Displays "9"
    };
    // undefines
    #undef a
    #undef b
    #undef c
    #undef d
    #undef e
    #undef f
    #undef g
    #undef h

    // Definitions for Olimex LCD digits 8 and 9
    #define a 0x01
    #define b 0x02
    #define c 0x04
    #define d 0x80
    #define e 0x40
    #define f 0x10
    #define g 0x20
    #define h 0x08
    // Character generator definition for display digits 8 and 9
    const char char_gen_8_9[] = {
        a+b+c+d+e+f,                            // 0 Displays "0"
        b+c,                                    // 1 Displays "1"
        a+b+d+e+g,                              // 2 Displays "2"
        a+b+c+d+g,                              // 3 Displays "3"
        b+c+f+g,                                // 4 Displays "4"
        a+c+d+f+g,                              // 5 Displays "5"
        a+c+d+e+f+g,                            // 6 Displays "6"
        a+b+c,                                  // 7 Displays "7"
        a+b+c+d+e+f+g,                          // 8 Displays "8"
        a+b+c+d+f+g,                            // 9 Displays "9"
    };
    // undefines
    #undef a
    #undef b
    #undef c
    #undef d
    #undef e
    #undef f
    #undef g
    #undef h

    // Definitions for Olimex LCD digits 1 to 7. Here each digit definition require 2 bytes
    #define a 0x0080
    #define b 0x0040
    #define c 0x0020
    #define d 0x0010
    #define e 0x2000
    #define f 0x4000
    #define g 0x0402
    #define h 0x1000
    // Character generator definition for display digits 1 to 7
    const int char_gen_1_7[] = {
        a+b+c+d+e+f,                            // 0 Displays "0"
        b+c,                                    // 1 Displays "1"
        a+b+d+e+g,                              // 2 Displays "2"
        a+b+c+d+g,                              // 3 Displays "3"
        b+c+f+g,                                // 4 Displays "4"
        a+c+d+f+g,                              // 5 Displays "5"
        a+c+d+e+f+g,                            // 6 Displays "6"
        a+b+c,                                  // 7 Displays "7"
        a+b+c+d+e+f+g,                          // 8 Displays "8"
        a+b+c+d+f+g,                            // 9 Displays "9"
    };
    // undefines
    #undef a
    #undef b
    #undef c
    #undef d
    #undef e
    #undef f
    #undef g
    #undef h


    int itobcd(int i)                           // Convert hex word to BCD.
    {   
      int bcd = 0;                              //
      char j = 0;                               //
     
      while (i > 9)                             //
      {
        bcd |= ((i % 10) << j);                 //
        i /= 10;                                //
        j += 4;
      }                                         //
      return (bcd | (i << j));                  // Return converted value
    }// itobcd(i)


    const unsigned char hex_table[] =
    {
        NUM_0,NUM_1,NUM_2,NUM_3,NUM_4,NUM_5,NUM_6,NUM_7,
        NUM_8,NUM_9,NUM_A,NUM_B,NUM_C,NUM_D,NUM_E,NUM_F
    };

    int32_t mul16(register int16_t x, register int16_t y);
                                               
    //FIR filter coefficient for removing 50/60Hz and 100/120Hz from the signals
    #if 0
    static const int16_t coeffs[9] =
    {
        5225,
        5175,
        7255,
        9453,
        11595,
        13507,
        15016,
        15983,
        16315
    };
    #else
    static const int16_t coeffs[12] =
    {
        688,
        1283,
        2316,
        3709,
        5439,
        7431,
        9561,
        11666,
        13563,
        15074,
        16047,
        16384
    };
    #endif

    // SaO2 Look-up Table
    const unsigned int Lookup [43] = {100,100,100,100,99,99,99,99,99,99,98,98,98,98,
                                      98,97,97,97,97,97,97,96,96,96,96,96,96,95,95,
                                      95,95,95,95,94,94,94,94,94,93,93,93,93,93};
    //
    //  #define FIRST_STAGE_TARGET_HIGH         3900
    //  #define FIRST_STAGE_TARGET_LOW          3600
    //  #define FIRST_STAGE_TARGET_HIGH_FINE    4096
    //  #define FIRST_STAGE_TARGET_LOW_FINE     3500

    // LED Target Range
    #define FIRST_STAGE_TARGET_HIGH         3500
    #define FIRST_STAGE_TARGET_LOW          3000
    #define FIRST_STAGE_TARGET_HIGH_FINE    4096
    #define FIRST_STAGE_TARGET_LOW_FINE     2700
    #define FIRST_STAGE_STEP                5
    #define FIRST_STAGE_FINE_STEP           1

    // UART Transmission Structure Definition
    enum scope_type_e
    {
        SCOPE_TYPE_OFF = 0,
        SCOPE_TYPE_HEART_SIGNALS,
        SCOPE_TYPE_RAW_SIGNALS,
        SCOPE_TYPE_LED_DRIVE,
    };
    int scope_type = SCOPE_TYPE_HEART_SIGNALS;
    //int scope_type = SCOPE_TYPE_RAW_SIGNALS;

    int ir_dc_offset = 2000;
    int vs_dc_offset = 2000;
    int ir_LED_level;
    int vs_LED_level;
    int ir_sample;
    int vs_sample;
    char is_IR;
    int ir_heart_signal;
    int vs_heart_signal;
    int ir_heart_ac_signal;
    int vs_heart_ac_signal;
    unsigned int rms_ir_heart_ac_signal;
    unsigned int rms_vs_heart_ac_signal;
    int32_t ir_2nd_dc_register = 0;
    int32_t vs_2nd_dc_register = 0;
    unsigned long log_sq_ir_heart_ac_signal;
    unsigned long log_sq_vs_heart_ac_signal;
    unsigned long sq_ir_heart_ac_signal;
    unsigned long sq_vs_heart_ac_signal;
    unsigned int pos_edge = 0;
    unsigned int edge_debounce;
    unsigned int heart_beat_counter;
    unsigned int log_heart_signal_sample_counter;
    unsigned int heart_signal_sample_counter;

    volatile unsigned int j;

    /* The results */
    unsigned int heart_rate;
    unsigned int heart_rate_LSB = 0;
    unsigned int SaO2, Ratio;
    unsigned int SaO2_LSB = 0;

    /* Function prototypes */
    //unsigned long isqrt32(register unsigned long h);
    int16_t dc_estimator(register int32_t *p, register int16_t x);
    int16_t ir_filter(int16_t sample);
    int16_t vs_filter(int16_t sample);
    void set_LCD(void);
    void display_number(int value, int start, int width);
    void display_pulse(int on);
    void display_correcting(int x, int on);

    void delay(long cycles){
      while(cycles){  cycles--; }
    }

    void main(void)
    {
        double f1;
        int32_t x;
        int32_t y;    
        
        WDTCTL = WDTPW | WDTHOLD;
        SCFI0 |= FN_4;                          // x2 DCO frequency, 8MHz nominal
                                                // DCO
        SCFQCTL = 91;                           // 32768 x 2 x (91 + 1) = 6.03 MHz
        FLL_CTL0 = DCOPLUS + XCAP10PF;          // DCO+ set so freq = xtal x D x
                                                //(N + 1)
        // Loop until 32kHz crystal stabilizes
        do
        {
          IFG1 &= ~OFIFG;                       // Clear oscillator fault flag
          for (j = 50000; j; j--);              // Delay
        }
        while (IFG1 & OFIFG);                   // Test osc fault flag
        
        // Setup GPIO
        P1DIR = 0xFF;
        P1OUT = 0;
        P2DIR = 0xFF;
        P2DIR |= BIT2 + BIT3;                   // P2.2 and P2.3 o/p direction -
                                                // drives PNP transistors in H-Bridge
        P2OUT = 0;
        P3DIR = 0xFF;
        P3OUT = 0;
        P4DIR = 0xFF;
        P4OUT = 0;
        P5DIR = 0xFF;
        P5OUT = 0;
        P6OUT = 0;
        
        /* Setup LCD */
        set_LCD();
        
        /* First amplifier stage - transconductance configuration */
        P6SEL |= (BIT0 | BIT1 | BIT2);          // Select OA0O
                                                // -ve=OA0I0, +ve=OA0I1
        OA0CTL0 = OAN_0 | OAP_1 | OAPM_3 | OAADC1;
        OA0CTL1 = 0;    
        
        /* Second amplifier stage */
        P6SEL |= (BIT3 | BIT4);                 // Select 0A1O 0A1I
                                                // -ve=OA1I0, +ve=DAC1
                                                // -ve=OA1I0, +ve=DAC1
    //    OA1CTL0 = OAN_0 | OAP_3 | OAPM_3 | OAADC1;
    //    OA1CTL1 = 0x00;   
                                                // Inverted input internally
                                                // connected to OA0 output
        OA1CTL0 = OAN_2 + OAP_3 + OAPM_3 + OAADC1;
        OA1CTL1 = OAFBR_7 + OAFC_6;             // OA as inv feedback amp, internal
                                                // gain = 15;    
        
        /* Configure DAC 1 to provide bias for the amplifier */
        P6SEL |= BIT7;
        DAC12_1CTL = DAC12CALON | DAC12IR | DAC12AMP_7 | DAC12ENC;
        DAC12_1DAT = 0;
        
        /* Configure DAC 0 to provide variable drive to the LEDs */
        DAC12_0CTL =  DAC12CALON | DAC12IR | DAC12AMP_7 | DAC12ENC; // VRef+, high speed/current,
                                                // DAC12OPS=0 => DAC12_0 output on P6.6 (pin 5) */
                                                // Configure P2.2 and P2.3 to
                                                // provide variable drive to LEDs
        P2OUT |= BIT2;                          // turn off source for D2
        P2OUT &= ~BIT3;                         // turn on source for D3        
        DAC12_0DAT = 3340;    

                                                // Set initial values for the LED brightnesses
        ir_LED_level = 1300;
        vs_LED_level = 1450;    
        
        /* Configure ADC12 */
        ADC12CTL0 &= ~ENC;                      // Enable conversions
                                                // Turn on the ADC12, and
                                                // set the sampling time
        ADC12CTL0 = ADC12ON + MSC + SHT0_4 + REFON + REF2_5V;
        ADC12CTL1 = SHP + SHS_1 + CONSEQ_1;     // Use sampling timer, single sequence,
                                                // TA1 trigger(SHS_1), start with ADC12MEM0
        ADC12MCTL0 = INCH_1 + SREF_1;           // ref+=Vref, channel = A1 = OA0
        ADC12MCTL1 = INCH_3 + SREF_1 + EOS;     // ref+=Vref, channel = A3 = OA1
        ADC12IE = BIT1;                         // ADC12MEM1 interrupt enable
        ADC12CTL0 |= ENC;                       // Enable the ADC
        ADC12CTL0 |= ADC12SC;                   // Start conversion    
        
        /* Configure Timer */
        TACTL = TASSEL0 + TACLR;                // ACLK, clear TAR,
        TACCTL1 = OUTMOD_2;
        TACCTL0 = CCIE;
                                                // This gives a sampling rate of
                                                // 512sps
        TACCR0 = 31;                            // Do two channels, at
                                                // 512sps each.
        TACCR1 = 10;                            // Allow plenty of time for the
                                                // signal to become stable before
                                                // sampling   
        TACTL |= MC_1;                          // Timer A on, up mode     
                                                
        /*Configure USART, so we can report readings to a PC */
        P2DIR |= BIT4;
        P2SEL |= BIT4;
        
        UCTL0 |= SWRST;
        ME1 |= UTXE0;                           // Enable USART1 TXD
        UCTL0 |= CHAR;                          // 8-bit char, SWRST=1
        UTCTL0 |= SSEL1;                        // UCLK = SMCLK
        UBR00 = 52;                             // 115200 from 6.02MHz = 52.33
        UBR10 = 0x00;
        UMCTL0 = 0x45;                          // Modulation = 0.375
        UCTL0 &= ~SWRST;                        // Initialise USART

    /*
        // For Olimex's LCD debug purpose only!
        int j=999;    
        for(int i=0;i<10;i++){
          delay(700000);
          display_number(j, 3, 3);              // The Small digits
          display_number(j, 7, 3);              // The Large digits
          j = j-111;
        }
        set_LCD();
    */    
        
        while(1)
        {
            __bis_SR_register(LPM0_bits + GIE);
     
            /* Heart Rate Computation */
            f1 = 60.0*512.0*3.0/(float)log_heart_signal_sample_counter;
            heart_rate = (unsigned int)f1;
            //heart_rate = f1;
            display_number(heart_rate, 3, 3);
            heart_rate_LSB = heart_rate & 0x00FF;
            
            /* SaO2 Computation */
            x = log_sq_ir_heart_ac_signal/log_heart_signal_sample_counter;
            y = log_sq_vs_heart_ac_signal/log_heart_signal_sample_counter;   
            Ratio = (unsigned int) (100.0*log(y)/log(x));                                         
            if (Ratio > 66)                                                                                                                                       //=======================================================
              SaO2 = Lookup[Ratio - 66];        // Ratio - 50 (Look-up Table Offset) - 16 (Ratio offset)       //========RATIO OFFSET =16 how it comes====================
            else if (Ratio > 50)                                                                                                                               //=======================================================
              SaO2 = Lookup[Ratio - 50];        // Ratio - 50 (Look-up Table Offset)                                         //   ===================================================
            else
              //SaO2 = 100;
              SaO2 = 99;
              display_number(SaO2, 7, 3);
              SaO2_LSB = SaO2 & 0x00FF;
        }
    }


    // Timer A0 interrupt service routine
    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A0(void)
    {
        int i;
        if ((DAC12_0CTL & DAC12OPS))            // D2 enabled in demo board
        {
                                                // Immediately enable the visible
                                                // LED, to allow time for the
                                                // transimpedance amp to settle
            DAC12_0CTL &= ~DAC12ENC;
            P2OUT &= ~BIT3;                     // turn on source for D3
            DAC12_0CTL &= ~DAC12OPS;            // Disable IR LED, enable visible LED
            DAC12_0CTL |= DAC12ENC;
            DAC12_0DAT = vs_LED_level;
            DAC12_1DAT = vs_dc_offset;          // Load op-amp offset value for visible
            P2OUT |= BIT2;                      // turn off source for D2        
            
            is_IR = 0;                          // IR LED OFF
            
            ir_sample = ADC12MEM0;              // Read the IR LED results
            i = ADC12MEM1;
                                                // Enable the next conversion sequence.
                                                // The sequence is started by TA1
            ADC12CTL0 &= ~ENC;
            ADC12CTL0 |= ENC;  
            
                                                // Filter away 50/60Hz electrical pickup,
                                                // and 100/120Hz room lighting optical pickup
            ir_heart_signal = ir_filter(i);
                                                // Filter away the large DC
                                                // component from the sensor */
            ir_heart_ac_signal = ir_heart_signal - dc_estimator(&ir_2nd_dc_register, ir_heart_signal);  
            
            /* Bring the IR signal into range through the second opamp */    
            if (i >= 4095)
            {
                if (ir_dc_offset > 100)
                    ir_dc_offset--;
            }
            else if (i < 100)
            {
                if (ir_dc_offset < 4095)
                    ir_dc_offset++;
            }        

            sq_ir_heart_ac_signal += (mul16(ir_heart_ac_signal, ir_heart_ac_signal) >> 10);

                                                //Tune the LED intensity to keep
                                                //the signal produced by the first
                                                //stage within our target range.
                                                //We don't really care what the
                                                //exact values from the first
                                                //stage are. They need to be
                                                //quite high, because a weak
                                                //signal will give poor results
                                                //in later stages. However, the
                                                //exact value only has to be
                                                //within the range that can be
                                                //handled properly by the next
                                                //stage. */        
            
            if (ir_sample > FIRST_STAGE_TARGET_HIGH
                ||
                ir_sample < FIRST_STAGE_TARGET_LOW)
            {
                                                //We are out of the target range
                                                //Starting kicking the LED
                                                //intensity in the right
                                                //direction to bring us back
                                                //into range. We use fine steps
                                                //when we are close to the target
                                                //range, and coarser steps when
                                                //we are far away.
                if (ir_sample > FIRST_STAGE_TARGET_HIGH)
                {
                    if (ir_sample >= FIRST_STAGE_TARGET_HIGH_FINE)
                        ir_LED_level -= FIRST_STAGE_STEP;
                    else
                        ir_LED_level -= FIRST_STAGE_FINE_STEP;
                                                // Clamp to the range of the DAC
                    if (ir_LED_level < 0)
                        ir_LED_level = 0;
                }
                else
                {
                    if (ir_sample < FIRST_STAGE_TARGET_LOW_FINE)
                        ir_LED_level += FIRST_STAGE_STEP;
                    else
                        ir_LED_level += FIRST_STAGE_FINE_STEP;
                                                // Clamp to the range of the DAC
                    if (ir_LED_level > 4095)
                        ir_LED_level = 4095;
                }
            }

            /* UART Transmission - IR heart signals */      
            switch (scope_type)
            {
            case SCOPE_TYPE_HEART_SIGNALS:
                i = (ir_heart_ac_signal >> 6) + 128;
                                                // Saturate to a byte
                if (i >= 255)                   // Make sure the data != 0x0 or 0xFF
                    i = 254;                    // as 0x0 and 0xFF are used for sync
                else if (i <= 0)                // bytes in the LABVIEW GUI
                    i = 1;
                
               TXBUF0 = 0x00;                   // Byte 1 - 0x00 (synchronization byte)
               while (!(IFG1 & UTXIFG0));
               TXBUF0 = 0xFF;                   // Byte 2 - 0xFF (synchronization byte)
               while (!(IFG1 & UTXIFG0));
               TXBUF0 = i;                      // Byte 3 - IR Heart signal (AC only)
               while (!(IFG1 & UTXIFG0));
               TXBUF0 = heart_rate_LSB;         // Byte 4 - Heart rate data
               while (!(IFG1 & UTXIFG0));
               TXBUF0 = SaO2_LSB;               // Byte 5 - %SaO2 data
               break;
                
            case SCOPE_TYPE_RAW_SIGNALS:
                while (!(IFG1 & UTXIFG0));            
                TXBUF0 = ir_sample >> 4;
                break;
            case SCOPE_TYPE_LED_DRIVE:
                TXBUF0 = ir_LED_level >> 4;
                break;
            }   
            
            /* Track the beating of the heart */
            heart_signal_sample_counter++;
            if (pos_edge)
            {
                if (edge_debounce < 120)
                {
                    edge_debounce++;
                }
                else
                {
                    if (ir_heart_ac_signal < -200)
                    {
                        edge_debounce = 0;
                        pos_edge = 0;
                        display_pulse(0);
                    }
                }
            }
            else
            {
                if (edge_debounce < 120)
                {
                    edge_debounce++;
                }
                else
                {
                    if (ir_heart_ac_signal > 200)
                    {
                        edge_debounce = 0;
                        pos_edge = 1;
                        display_pulse(1);
                        //display_correcting(1, 0);
                        if (++heart_beat_counter >= 3)
                        {
                            log_heart_signal_sample_counter = heart_signal_sample_counter;
                            log_sq_ir_heart_ac_signal = sq_ir_heart_ac_signal;
                            log_sq_vs_heart_ac_signal = sq_vs_heart_ac_signal;
                            heart_signal_sample_counter = 0;
                            sq_ir_heart_ac_signal = 0;
                            sq_vs_heart_ac_signal = 0;
                            heart_beat_counter = 0;
                            _BIC_SR_IRQ(LPM0_bits);
                                                // Do a dummy wake up roughly
                                                // every 2 seconds
                        }
                    }
                }
            }
        }
        else                                    //D3 enabled in demoboard
        {
                                                //Immediately enable the IR LED,
                                                //to allow time for the
                                                //transimpedance amp to settle */
            DAC12_0CTL &= ~DAC12ENC;
            P2OUT &= ~BIT2;                     //turn on source for D3        
            DAC12_0CTL |= DAC12OPS;             // Disable visible LED, enable IR LED
            DAC12_0CTL |= DAC12ENC;
            DAC12_0DAT = ir_LED_level;
            DAC12_1DAT = ir_dc_offset;          // Load op-amp offset value for IR
            P2OUT |= BIT3;                      //turn off source for D2
            
            is_IR = 1;                          // IR LED ON
            
            vs_sample = ADC12MEM0;              //Read the visible LED results
            i = ADC12MEM1;
            
                                                //Enable the next conversion sequence.
                                                //The sequence is started by TA1
            ADC12CTL0 &= ~ENC;
            ADC12CTL0 |= ENC;  


                                                //Filter away 50/60Hz electrical
                                                //pickup, and 100/120Hz room
                                                //lighting optical pickup */
            vs_heart_signal = vs_filter(i);
                                                //Filter away the large DC
                                                //component from the sensor */
            vs_heart_ac_signal = vs_heart_signal - dc_estimator(&vs_2nd_dc_register, vs_heart_signal);
            
           /* Bring the VS signal into range through the second opamp */
            if (i >= 4095)
            {
                if (vs_dc_offset > 100)
                    vs_dc_offset--;
            }
            else if (i < 100)
            {
                if (vs_dc_offset < 4095)
                    vs_dc_offset++;
            }        

            sq_vs_heart_ac_signal += (mul16(vs_heart_ac_signal, vs_heart_ac_signal) >> 10);
            
            if (vs_sample > FIRST_STAGE_TARGET_HIGH
                ||
                vs_sample < FIRST_STAGE_TARGET_LOW)
            {
                /* We are out of the target range */
                //display_correcting(1, 1);
                if (vs_sample > FIRST_STAGE_TARGET_HIGH)
                {
                    if (vs_sample >= FIRST_STAGE_TARGET_HIGH_FINE)
                        vs_LED_level -= FIRST_STAGE_STEP;
                    else
                        vs_LED_level -= FIRST_STAGE_FINE_STEP;
                    if (vs_LED_level < 0)
                        vs_LED_level = 0;
                }
                else
                {
                    if (vs_sample < FIRST_STAGE_TARGET_LOW_FINE)
                        vs_LED_level += FIRST_STAGE_STEP;
                    else
                        vs_LED_level += FIRST_STAGE_FINE_STEP;
                    if (vs_LED_level > 4095)
                        vs_LED_level = 4095;
                }
            }
        }
           
    }
        
    #pragma vector=ADC_VECTOR
    __interrupt void ADC12ISR(void)
    {
        ADC12IFG &= ~BIT1;                      // Clear the ADC12 interrupt flag
        DAC12_0DAT = 0;                         // Turn OFF the LED
        DAC12_1DAT = 0;
        // Turn OFF the H-Bridge completely
        if(is_IR)                               // If IR LED was ON in TA0 ISR
          P2OUT |= BIT2;                        // P2.2 = 1
        else                                    // Else if VS LED ON in TA0 ISR
          P2OUT |= BIT3;                        // P2.3 = 1
    }

    int16_t ir_filter(int16_t sample)
    {
        static int16_t buf[32];
        static int offset = 0;
        int32_t z;
        int i;
                                                //Filter hard above a few Hertz,
                                                //using a symmetric FIR.
                                                //This has benign phase
                                                //characteristics */
        buf[offset] = sample;
        z = mul16(coeffs[11], buf[(offset - 11) & 0x1F]);
        for (i = 0;  i < 11;  i++)
            z += mul16(coeffs[i], buf[(offset - i) & 0x1F] + buf[(offset - 22 + i) & 0x1F]);
        offset = (offset + 1) & 0x1F;
        return  z >> 15;
    }

    int16_t vs_filter(int16_t sample)
    {
        static int16_t buf[32];
        static int offset = 0;
        int32_t z;
        int i;

                                                //Filter hard above a few Hertz,
                                                //using a symmetric FIR.
                                                //This has benign phase
                                                //characteristics */
        buf[offset] = sample;
        z = mul16(coeffs[11], buf[(offset - 11) & 0x1F]);
        for (i = 0;  i < 11;  i++)
            z += mul16(coeffs[i], buf[(offset - i) & 0x1F] + buf[(offset - 22 + i) & 0x1F]);
        offset = (offset + 1) & 0x1F;
        return  z >> 15;
    }

    /*unsigned long isqrt32(register unsigned long h)
    {
        register unsigned long x;
        register unsigned long y;
        register int i;

                                                //Calculate a 32 bit bit square
                                                //root of a 32 bit integer,
                                                //where the top 16 bits
                                                //of the result is the integer
                                                //part of the result, and the
                                                //low 16 bits are fractional.
        x =
        y = 0;
        for (i = 0;  i < 32;  i++)
        {
            x = (x << 1) | 1;
            if (y < x)
                x -= 2;
            else
                y -= x;
            x++;
            y <<= 1;
            if ((h & 0x80000000))
                y |= 1;
            h <<= 1;
            y <<= 1;
            if ((h & 0x80000000))
                y |= 1;
            h <<= 1;
        }
        return  x;
    } */

    int16_t dc_estimator(register int32_t *p, register int16_t x)
    {
        /* Noise shaped DC estimator. */
        *p += ((((int32_t) x << 16) - *p) >> 9);
        return (*p >> 16);
    }