IEC60601-1-8 Audible Alert Generator

Dependencies:   mbed

See here for more info.

IEC60601-1-8.cpp

Committer:
wim
Date:
2012-05-18
Revision:
0:07767204347b

File content as of revision 0:07767204347b:

/****************************************************************
 *  IEC601601-1-8 
 *
 *   This modules provides the IEC Medical Alert tones.
 *
 *
 *   Copyright(C) 2007, NXP Semiconductor
 *   All rights reserved.
 *
 *   Port to mbed 2012 (WH)
 ****************************************************************/
#include "mbed.h"
#include "IEC60601-1-8.h"
 
#define DEBUG_ON       0           // Activate Testpins for timing 
#if(DEBUG_ON)
// Activate Testpins for timing 
DigitalOut TimInt(p19);  
DigitalOut SeqInt(p20);  
#endif
 
#define AMPL           200         // Output Amplitude     
#define PI             3.1415926
#define FSAMPLE        25000       // Timer  Reload Frequency               
  
 
// define rise and fall time of tones
#define HP_RISE        12          // rise time setting for high priority tones (12 = ~20ms Tr)
                                   //   decrease to make slower, increase to make faster
#define HP_FALL        12          // fall time setting for high priority tones (12 = ~20ms Tf)
                                   //   decrease to make slower, increase to make faster
#define MP_RISE        8           // rise time setting for high priority tones (8 = ~30ms Tr)
                                   //   decrease to make slower, increase to make faster
#define MP_FALL        8           // fall time setting for high priority tones (8 = ~30ms Tf)
                                   //   decrease to make slower, increase to make faster


Note_Type const _TuneSequence [][5] = {{C4,C4,C4,C4,C4},           // general
                                       {C5,B4,A4,G4,F4},           // oxygen
                                       {C4,A4,F4,A4,F4},           // ventilation
                                       {C4,E4,G4,G4,C5},           // cardiovascular
                                       {C4,D4,E4,F4,G4},           // temperature
                                       {C5,D4,G4,C5,D4},           // drug_delivery
                                       {C4,Fsharp4,C4,C4,Fsharp4}, // perfusion
                                       {C5,C4,C4,C5,C4},           // power_fail
//                                      {E4,C4,0,0,0}};             // low_alarm
                                       {E4,C4,C4,C4,C4}};          // low_alarm                                           

float const _FreqArray[][5]= {{261.626,523.252,784.878,1046.50,1308.13},     // C4
                              {293.67,587.34,881.01,1174.7,1468.3},          // D4
                              {329.63,659.26,988.89,1318.52,1648.15},        // E4
                              {349.23,698.46,1047.69,1396.9,1746.15},        // F4
                              {369.99,739.98,1109.97,1479.96,1849.95},       // FSharp4
                              {392.00,784.00,1176.0,1568.0,1960.0},          // G4
                              {440.000,880.00,1320.0,1760.00,2200.00},       // A4
                              {493.88,987.76,1481.64,1975.5,2469.4},         // B4
                              {523.251,1046.50,1569.756,2093.00,2616.25}};   // C5
      
unsigned char _ToneWeights[] = {255,255,255,255,255};    // used for test and  
                                                         // adjusting harmonic levels

struct wave _Waves[9][5];    // 'Waves' holds tone gen coefficients and variables
                             // the coefficients are calculated during initialization.


IEC60601::IEC60601() {
    
    _InitDAC();     
    _InitSequencer();
    _InitToneCoefArray();
    _ticker.attach_us(this, &IEC60601::_TimerInteruptHandler, 40);
};



void IEC60601::TurnOnAlarm(Prio_Type priority, Alarm_Type alarm_type) {
   
  _priority = priority;
  _alarm_type = alarm_type;
  
  _mscount = 0;    
  _sequence = 1;

}

void IEC60601::TurnOffAlarm(void) {
  _sequence = 0;
}

void IEC60601::TestAlarm(Note_Type active_note, int w0, int w1, int w2, int w3, int w4) {         
         
  _priority = TEST;
    
  _active_note = active_note;
  _ToneWeights[0]= w0;
  _ToneWeights[1]= w1;
  _ToneWeights[2]= w2;
  _ToneWeights[3]= w3;
  _ToneWeights[4]= w4;
  
  _mscount = 0;    
  _sequence = 1;
}


// This modules provides the note sequencers and envelope control 
// functions for the alarm notes for the IEC Medical Alert tone demo.

void IEC60601::_InitSequencer(void) {
  _envelope_on = false;
  _envelope_off = false;
}


void IEC60601::_TurnOnNote(void) {
  _envelope = 0;          
  _note_on = true;
  _envelope_on = true;
}

void IEC60601::_TurnOffNote(void) {
  _note_on = false;
}


void IEC60601::_HighPriSequence (void) {
   
   switch (_mscount) {
      case 1:
        _active_note = _TuneSequence [_alarm_type][0];  // 1rst note of sequence
        _note_level = 170;
        _TurnOnNote();    
        break;      
      case 145:                       // 145 ms (trise + tduration)
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 224:
        _active_note = _TuneSequence [_alarm_type][1];  // 2nd note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 368:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 447:
        _active_note = _TuneSequence [_alarm_type][2];  // 3rd note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 591:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 835:
        _active_note = _TuneSequence [_alarm_type][3];  // 4th note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 979:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 1058:
        _active_note = _TuneSequence [_alarm_type][4];  // 5th note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 1202:
        _note_on = false;             // begin decay as note turns "off"    
        break;
        
      case 1250:                      // allows for fall time of envelope
        if (_sequence == 2) {         // Done after one repeat 
          _sequence = 0;
          _mscount = 0;
        }  
        break;                  
      case 1750:
        if (_sequence == 1) {         // If this is the first time through, repeat
          _sequence = 2;    
        } 
        _mscount = 0;
        break;    
   }
}


void IEC60601::_MedPriSequence (void) {

   switch (_mscount) {
      case 1:
        _active_note = _TuneSequence [_alarm_type][0];
        _note_level = 170;
        _TurnOnNote();    
        break;    
      case 221:                       // 221 ms (trise(30) + tduration(190)+ start(1))
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 441:
        _active_note = _TuneSequence [_alarm_type][1];  // 2nd note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 661:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 881:
        _active_note = _TuneSequence [_alarm_type][2];  // 3rd note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 1101:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 1151:                      // allows for fall time of envelope 
        _sequence = 0;                // Medium Prio does not repeat 
        _mscount = 0;
    }
    
}

void IEC60601::_LowPriSequence (void) {

   switch (_mscount) {
      case 1:
        _active_note = _TuneSequence [_alarm_type][0];
        _note_level = 170;
        _TurnOnNote();    
        break;     
      case 261:                       // 261 ms (trise(35) + tduration(225))
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 521:
        _active_note = _TuneSequence [_alarm_type][1];  // 2nd note of sequence
        _note_level = 255;
        _TurnOnNote();
        break;
      case 781:
        _note_on = false;             // begin decay as note turns "off"    
        break;
      case 816:                       // Low Prio does not repeat  
        _sequence = 0;
        _mscount = 0;
    }
}

void IEC60601::_TestSequence (void) {

   switch (_mscount) {
      case 1:
//      _active_note = _TuneSequence [_alarm_type][0];
        _note_level = 255;
        _TurnOnNote();    
        break;
      
      case 1000:             
        _note_on = false;             // begin decay as note turns "off"    
        break;

      case 1035:

         for (int i=0; i<5; i++) {    // Restore weights
           _ToneWeights[i] = 255;
         };
        _sequence = 0;                // Test does not repeat  
        _mscount = 0;
        break;
    }
}

void IEC60601::_EnvelopeControl(void) {

   if (_note_on) {
        if (_envelope >= _note_level) {
         _envelope = _note_level;
     }
     else {
        if (_priority == HIGH) {
            _envelope += HP_RISE;        // high priority risetime control
        }
        else {
          _envelope += MP_RISE;          // Medium priority risetime control
        }
     }
   }
  else {
      if (_envelope > 0) {
        if (_priority == HIGH) {
            _envelope -= HP_FALL;        // high priority falltime control
        }
        else {
          _envelope -= MP_FALL;          // Medium priority falltime control
        }
      }
  }
  if ((_envelope <= 0) && (!_note_on) && (_envelope_on) ) {
    _envelope = 0;
    _envelope_off = true;              // synchronize with zero cross
  }
}



// This module generates multiple sine waves that are combined
// to generate tones that contain a fundamental and 4 harmonics
// per the IEC60601-1-8 Medical Alarm specification 

void IEC60601::_InitDAC(void) { 

    LPC_PINCON->PINSEL1  &= ~0x00300000;         // enable DAC P0.26
    LPC_PINCON->PINSEL1  |=  0x00200000;         
    LPC_PINCON->PINMODE1 &= ~0x00300000;         // disable Rs on P0.26
    LPC_PINCON->PINMODE1 |=  0x00200000;         

    LPC_DAC->DACR = 0x8000;                  // init DAC to half on voltage
}


void IEC60601::_InitToneCoefArray(void) {      // generate the coefficients and init array for tones 

  unsigned char n;
  unsigned char j;

  for (j=0;j<9;j++)                // Initialize all nine scale tones (C4-C5)
  {
     for (n=0;n<5;n++)             // fundamental and 4 harmonics for IEC60601-1-8
     {
        _Waves[j][n].coef = ((cos (2*PI*(float)(_FreqArray[j][n]/FSAMPLE)))* 32768) ;       // 2* taken out, put in final calc as a shift
        _Waves[j][n].y1   = 0;
        _Waves[j][n].y2   = ((sin (2*PI*(float)((_FreqArray[j][n]/FSAMPLE))) * AMPL * 32768));    // Try 8388608 (+8 bits) w/ long coef
     }
  }
}


void IEC60601::_GenerateMultiTone (struct wave *t) {
  long int y;
  int i;
  int env_weights;
  long int output;
  static long int output_old;

  output = 0;                // clear output accumulator 
  for (i=0; i<5; i++) {        // cycle through the 5 structures in the array
    y = ((t->coef *(long long)(t->y1)>>14)) - t->y2;   // Goertzel Calculation
    t->y2 = t->y1;                                     // store for next time
    t->y1 = y;                                         // store for next time
    env_weights = _envelope * _ToneWeights[i]>>8;
    output += ((t->y1 * env_weights) >> 8);      // sum fundamental and harmonics
    t++;                                      // increment structure pointer
  }

////  DAC->DACR = ((output >> 10) & 0xFFC0) + 0x8000;  // make unsigned and output to DAC
///// Adapt to mbed !!!!
  LPC_DAC->DACR = ((output >> 10) & 0xFFC0) + 0x8000;  // make unsigned and output to DAC

  
  if ((output >= 0) && (output_old <= 0)) {  // zero crossing detect
     if (_envelope_off && (!_note_on)) {
        _envelope_on = false;      // sychronizes turn off with zero cross
        _envelope_off = false;     // reset envelope flag 
     }
  }     
  output_old = output;
}

#if(0)
void IEC60601::_OutputTones(Note_Type note, unsigned char level) {

  _note_level = level;                   
  _GenerateMultiTone (&Waves[note][0]);
}
#endif


void IEC60601::_TimerInteruptHandler (void){    
  static int timeval = 0;
  
#if(DEBUG_ON)
// Activate Testpins for timing 
    TimInt = 1;  
#endif
  
    if (_envelope_on) {
//Oude code 
//      _OutputTones(_active_note, _note_level); // parameters are set in sequencer
      
        _GenerateMultiTone (&_Waves[_active_note][0]);    // parameters set in sequencer     
    }            

    timeval++;
    if (timeval == 25) {          // millisecond interval (@ 25 khz sample rate)

#if(DEBUG_ON)
        // Activate Testpins for timing 
        SeqInt=1;  
#endif  
        if (_sequence != 0) {
            switch (_priority) {
                 case HIGH:
                     _HighPriSequence();
                    break;
                 case MEDIUM:
                     _MedPriSequence();
                    break;
                 case LOW:
                     _LowPriSequence();
                    break;
                case TEST:
                     _TestSequence();
                    break;
               }
        }
        timeval = 0;            // clear interval counter
        _mscount++;             // increment ms counter
        _EnvelopeControl();        

#if(DEBUG_ON)
        // Activate Testpins for timing 
        SeqInt=0;  
#endif
    }

#if(DEBUG_ON)
// Activate Testpins for timing 
    TimInt = 0;  
#endif

}