/* * ipmi_sensor_factors.cpp * * Copyright (c) 2004 by FORCE Computers * Copyright (c) 2005 by ESO Technologies. * * Note that this file is based on parts of OpenIPMI * written by Corey Minyard * of MontaVista Software. Corey's code was helpful * and many thanks go to him. He gave the permission * to use this code in OpenHPI under BSD license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This * file and program are licensed under a BSD style license. See * the Copying file included with the OpenHPI distribution for * full licensing terms. * * Authors: * Thomas Kanngieser * Pierre Sangouard */ #include "ipmi_sensor_factors.h" #include static const char *analoge_data_format[] = { "Unsigned", "1Compl", "2Compl", "Analog" }; const char * IpmiAnalogeDataFormatToString( tIpmiAnalogeDataFormat fmt ) { if ( (int)fmt <= eIpmiAnalogDataFormatNotAnalog ) return analoge_data_format[fmt]; return "Invalid"; } static const char *linearization_map[] = { "Linear", "Ln", "Log10", "Log2", "E", "Exp10", "Exp2", "1OverX", "Sqr", "Cube", "Sqrt", "1OverCube" }; const char * IpmiLinearizationToString( tIpmiLinearization val ) { if ( val == eIpmiLinearizationNonlinear ) return "NonLinear"; if ( val <= eIpmiLinearization1OverCube ) return linearization_map[val]; return "Invalid"; } cIpmiSensorFactors::cIpmiSensorFactors() : m_analog_data_format( eIpmiAnalogDataFormatUnsigned ), m_linearization( eIpmiLinearizationLinear ), m_is_non_linear( false ), m_m( 0 ), m_tolerance( 0 ), m_b( 0 ), m_r_exp( 0 ), m_accuracy_exp( 0 ), m_accuracy( 0 ), m_b_exp( 0 ) { } cIpmiSensorFactors::~cIpmiSensorFactors() { } bool cIpmiSensorFactors::GetDataFromSdr( const cIpmiSdr *sdr ) { m_analog_data_format = (tIpmiAnalogeDataFormat)((sdr->m_data[20] >> 6) & 3); m_linearization = (tIpmiLinearization)(sdr->m_data[23] & 0x7f); if ( m_linearization <= 11 ) { m_m = sdr->m_data[24] | ((sdr->m_data[25] & 0xc0) << 2); m_tolerance = sdr->m_data[25] & 0x3f; m_b = sdr->m_data[26] | ((sdr->m_data[27] & 0xc0) << 2); m_accuracy = ((sdr->m_data[27] & 0x3f) | ((sdr->m_data[28] & 0xf0) << 2)); m_accuracy_exp = (sdr->m_data[28] >> 2) & 0x3; m_r_exp = (sdr->m_data[29] >> 4) & 0xf; m_b_exp = sdr->m_data[29] & 0xf; m_accuracy_factor = (m_accuracy * pow( 10.0, m_accuracy_exp)) / 100.0; } if ( m_linearization == eIpmiLinearizationLinear ) m_is_non_linear = false; else m_is_non_linear = true; return true; } bool cIpmiSensorFactors::Cmp( const cIpmiSensorFactors &sf ) const { if ( m_analog_data_format != sf.m_analog_data_format ) return false; if ( m_linearization != sf.m_linearization ) return false; if ( m_linearization <= 11 ) { if ( m_m != sf.m_m ) return false; if ( m_tolerance != sf.m_tolerance ) return false; if ( m_b != sf.m_b ) return false; if ( m_accuracy != sf.m_accuracy ) return false; if ( m_accuracy_exp != sf.m_accuracy_exp ) return false; if ( m_r_exp != sf.m_r_exp ) return false; if ( m_b_exp != sf.m_b_exp ) return false; } return true; } static double c_linear( double val ) { return val; } static double c_exp10( double val ) { return pow( 10.0, val ); } static double c_exp2( double val ) { return pow( 2.0, val ); } static double c_1_over_x( double val ) { return 1.0 / val; } static double c_sqr( double val ) { return pow( val, 2.0 ); } static double c_cube( double val ) { return pow( val, 3.0 ); } static double c_1_over_cube( double val ) { return 1.0 / pow( val, 3.0 ); } // We have to define our own log2 function // because some versions of FreeBSD do not provide it // and some versions of FreeBSD provide it in a very // specific way. static double ipmi_log2( double val ) { return log( val ) / M_LN2; } typedef double (*linearizer)( double val ); static linearizer linearize[12] = { c_linear, log, log10, ipmi_log2, exp, c_exp10, c_exp2, c_1_over_x, c_sqr, c_cube, sqrt, c_1_over_cube }; static int sign_extend( int m, int bits ) { if ( m & (1 << (bits-1)) ) return m | (-1 << bits); else return m & (~(-1 << bits)); } bool cIpmiSensorFactors::ConvertFromRaw( unsigned int val, double &result, bool is_hysteresis) const { double m, b, b_exp, r_exp, fval; linearizer c_func; if ( m_linearization == eIpmiLinearizationNonlinear ) c_func = c_linear; else if ( m_linearization <= 11 ) c_func = linearize[m_linearization]; else return false; val &= 0xff; m = m_m; b = m_b; r_exp = m_r_exp; b_exp = m_b_exp; if ( is_hysteresis == true ) { if ( val == 0 ) { result = 0; return true; } // For hysteresis : no offset + abs value b = 0; if ( m < 0 ) m = -m; } switch( m_analog_data_format ) { case eIpmiAnalogDataFormatUnsigned: fval = val; break; case eIpmiAnalogDataFormat1Compl: val = sign_extend( val, 8 ); if ( val == 0xffffffff ) val += 1; fval = val; break; case eIpmiAnalogDataFormat2Compl: fval = sign_extend( val, 8 ); break; default: return false; } result = c_func( ((m * fval) + (b * pow(10, b_exp))) * pow(10, r_exp) ); return true; } bool cIpmiSensorFactors::ConvertToRaw( tIpmiRound rounding, double val, unsigned int &result, bool is_hysteresis, bool swap_thresholds ) const { bool rv; bool swap; double cval; int lowraw, highraw, raw, maxraw, minraw, next_raw; if (is_hysteresis == true) swap = false; else swap = swap_thresholds; switch( m_analog_data_format ) { case eIpmiAnalogDataFormatUnsigned: lowraw = 0; highraw = 255; minraw = 0; maxraw = 255; next_raw = 128; break; case eIpmiAnalogDataFormat1Compl: lowraw = -127; highraw = 127; minraw = -127; maxraw = 127; next_raw = 0; break; case eIpmiAnalogDataFormat2Compl: lowraw = -128; highraw = 127; minraw = -128; maxraw = 127; next_raw = 0; break; default: return false; } // We do a binary search for the right value. Yuck, but I don't // have a better plan that will work with non-linear sensors. do { raw = next_raw; rv = ConvertFromRaw( raw, cval, is_hysteresis ); if ( !rv ) return false; // If swap == true, when raw value increases // the corresponding interpreted value decreases // so we have to take that into account when searching if ((( swap == false) && ( cval < val )) || (( swap == true) && ( cval > val ))) { next_raw = ((highraw - raw) / 2) + raw; lowraw = raw; } else { next_raw = ((raw - lowraw) / 2) + lowraw; highraw = raw; } } while( raw != next_raw ); // The above loop gets us to within 1 of what it should be, we // have to look at rounding to make the final decision. switch( rounding ) { case eRoundNormal: // If swap == true, when raw value increases // the corresponding interpreted value decreases // so we have to take that into account when searching if ((( swap == false ) && ( val > cval )) || (( swap == true ) && ( val < cval ))) { if ( raw < maxraw ) { double nval; rv = ConvertFromRaw( raw + 1, nval, is_hysteresis ); if ( !rv ) return false; nval = cval + ((nval - cval) / 2.0); if ((( swap == false ) && ( val >= nval )) || (( swap == true ) && ( val <= nval ))) raw++; } } else { if ( raw > minraw ) { double pval; rv = ConvertFromRaw( raw-1, pval, is_hysteresis ); if ( !rv ) return false; pval = pval + ((cval - pval) / 2.0); if ((( swap == false ) && ( val < pval )) || (( swap == true ) && ( val > pval ))) raw--; } } break; case eRoundUp: // If swap == true, when raw value increases // the corresponding interpreted value decreases // so we have to take that into account when searching if ( swap == false ) { if ((val > cval) && (raw < maxraw)) raw++; } else { if ((val < cval) && (raw < maxraw)) raw++; }; break; case eRoundDown: // If swap == true, when raw value increases // the corresponding interpreted value decreases // so we have to take that into account when searching if ( swap == false ) { if ( ( val < cval) && (raw > minraw ) ) raw--; } else { if ( ( val > cval) && (raw > minraw ) ) raw--; }; break; } if ( m_analog_data_format == eIpmiAnalogDataFormat1Compl ) if ( raw < 0 ) raw -= 1; result = raw & 0xff; return true; }