13f7c5e40SJason M. Bills /*
23f7c5e40SJason M. Bills // Copyright (c) 2017 2018 Intel Corporation
33f7c5e40SJason M. Bills //
43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License");
53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License.
63f7c5e40SJason M. Bills // You may obtain a copy of the License at
73f7c5e40SJason M. Bills //
83f7c5e40SJason M. Bills //      http://www.apache.org/licenses/LICENSE-2.0
93f7c5e40SJason M. Bills //
103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software
113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS,
123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and
143f7c5e40SJason M. Bills // limitations under the License.
153f7c5e40SJason M. Bills */
163f7c5e40SJason M. Bills 
173f7c5e40SJason M. Bills #pragma once
1872867debSJason M. Bills #include <cmath>
1972867debSJason M. Bills #include <iostream>
203f7c5e40SJason M. Bills 
213f7c5e40SJason M. Bills namespace ipmi
223f7c5e40SJason M. Bills {
2372867debSJason M. Bills static constexpr int16_t maxInt10 = 0x1FF;
2439417c7bSJames Feist static constexpr int16_t minInt10 = -0x200;
2572867debSJason M. Bills static constexpr int8_t maxInt4 = 7;
2672867debSJason M. Bills static constexpr int8_t minInt4 = -8;
2772867debSJason M. Bills 
2872867debSJason M. Bills static inline bool getSensorAttributes(const double max, const double min,
2972867debSJason M. Bills                                        int16_t& mValue, int8_t& rExp,
3072867debSJason M. Bills                                        int16_t& bValue, int8_t& bExp,
3172867debSJason M. Bills                                        bool& bSigned)
3272867debSJason M. Bills {
3372867debSJason M. Bills     // computing y = (10^rRexp) * (Mx + (B*(10^Bexp)))
3472867debSJason M. Bills     // check for 0, assume always positive
3572867debSJason M. Bills     double mDouble;
3672867debSJason M. Bills     double bDouble;
3739417c7bSJames Feist     if (max <= min)
3872867debSJason M. Bills     {
39*53870d73SVernon Mauery         std::cerr << "getSensorAttributes: Max must be greater than min\n";
4072867debSJason M. Bills         return false;
4172867debSJason M. Bills     }
4239417c7bSJames Feist 
4372867debSJason M. Bills     mDouble = (max - min) / 0xFF;
4472867debSJason M. Bills 
4572867debSJason M. Bills     if (min < 0)
4672867debSJason M. Bills     {
4772867debSJason M. Bills         bSigned = true;
4872867debSJason M. Bills         bDouble = floor(0.5 + ((max + min) / 2));
4972867debSJason M. Bills     }
5072867debSJason M. Bills     else
5172867debSJason M. Bills     {
5272867debSJason M. Bills         bSigned = false;
5372867debSJason M. Bills         bDouble = min;
5472867debSJason M. Bills     }
5572867debSJason M. Bills 
5672867debSJason M. Bills     rExp = 0;
5772867debSJason M. Bills 
5872867debSJason M. Bills     // M too big for 10 bit variable
5972867debSJason M. Bills     while (mDouble > maxInt10)
6072867debSJason M. Bills     {
6139417c7bSJames Feist         if (rExp >= maxInt4)
6272867debSJason M. Bills         {
63*53870d73SVernon Mauery             std::cerr << "rExp Too big, Max and Min range too far REXP=" << rExp
64*53870d73SVernon Mauery                       << "\n";
6572867debSJason M. Bills             return false;
6672867debSJason M. Bills         }
6772867debSJason M. Bills         mDouble /= 10;
6839417c7bSJames Feist         rExp++;
6972867debSJason M. Bills     }
7072867debSJason M. Bills 
7172867debSJason M. Bills     // M too small, loop until we lose less than 1 eight bit count of precision
7272867debSJason M. Bills     while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255))
7372867debSJason M. Bills     {
7439417c7bSJames Feist         if (rExp <= minInt4)
7572867debSJason M. Bills         {
76*53870d73SVernon Mauery             std::cerr << "rExp Too Small, Max and Min range too close\n";
7772867debSJason M. Bills             return false;
7872867debSJason M. Bills         }
7972867debSJason M. Bills         // check to see if we reached the limit of where we can adjust back the
8072867debSJason M. Bills         // B value
8172867debSJason M. Bills         if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble)
8272867debSJason M. Bills         {
8372867debSJason M. Bills             if (mDouble < 1.0)
8472867debSJason M. Bills             {
85*53870d73SVernon Mauery                 std::cerr << "Could not find mValue and B value with enough "
86*53870d73SVernon Mauery                              "precision.\n";
8772867debSJason M. Bills                 return false;
8872867debSJason M. Bills             }
8972867debSJason M. Bills             break;
9072867debSJason M. Bills         }
9172867debSJason M. Bills         // can't multiply M any more, max precision reached
9272867debSJason M. Bills         else if (mDouble * 10 > maxInt10)
9372867debSJason M. Bills         {
9472867debSJason M. Bills             break;
9572867debSJason M. Bills         }
9672867debSJason M. Bills         mDouble *= 10;
9739417c7bSJames Feist         rExp--;
9872867debSJason M. Bills     }
9972867debSJason M. Bills 
10072867debSJason M. Bills     bDouble /= std::pow(10, rExp);
10172867debSJason M. Bills     bExp = 0;
10272867debSJason M. Bills 
10372867debSJason M. Bills     // B too big for 10 bit variable
10472867debSJason M. Bills     while (bDouble > maxInt10 || bDouble < minInt10)
10572867debSJason M. Bills     {
10639417c7bSJames Feist         if (bExp >= maxInt4)
10772867debSJason M. Bills         {
108*53870d73SVernon Mauery             std::cerr
109*53870d73SVernon Mauery                 << "bExp Too Big, Max and Min range need to be adjusted\n";
11072867debSJason M. Bills             return false;
11172867debSJason M. Bills         }
11272867debSJason M. Bills         bDouble /= 10;
11339417c7bSJames Feist         bExp++;
11472867debSJason M. Bills     }
11572867debSJason M. Bills 
11672867debSJason M. Bills     while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) >
11772867debSJason M. Bills            (1.0 / 255))
11872867debSJason M. Bills     {
11939417c7bSJames Feist         if (bExp <= minInt4)
12072867debSJason M. Bills         {
121*53870d73SVernon Mauery             std::cerr
122*53870d73SVernon Mauery                 << "bExp Too Small, Max and Min range need to be adjusted\n";
12372867debSJason M. Bills             return false;
12472867debSJason M. Bills         }
12572867debSJason M. Bills         bDouble *= 10;
12672867debSJason M. Bills         bExp -= 1;
12772867debSJason M. Bills     }
12872867debSJason M. Bills 
129aecaef7eSJames Feist     mValue = static_cast<int16_t>(std::round(mDouble)) & maxInt10;
13039417c7bSJames Feist     bValue = static_cast<int16_t>(bDouble) & maxInt10;
13172867debSJason M. Bills 
13272867debSJason M. Bills     return true;
13372867debSJason M. Bills }
13472867debSJason M. Bills 
13572867debSJason M. Bills static inline uint8_t
13672867debSJason M. Bills     scaleIPMIValueFromDouble(const double value, const uint16_t mValue,
1373f7c5e40SJason M. Bills                              const int8_t rExp, const uint16_t bValue,
13872867debSJason M. Bills                              const int8_t bExp, const bool bSigned)
13972867debSJason M. Bills {
14072867debSJason M. Bills     uint32_t scaledValue =
14172867debSJason M. Bills         (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) /
14272867debSJason M. Bills         (mValue * std::pow(10, rExp));
14339417c7bSJames Feist 
14439417c7bSJames Feist     if (scaledValue > std::numeric_limits<uint8_t>::max() ||
14539417c7bSJames Feist         scaledValue < std::numeric_limits<uint8_t>::lowest())
14639417c7bSJames Feist     {
14739417c7bSJames Feist         throw std::out_of_range("Value out of range");
14839417c7bSJames Feist     }
14972867debSJason M. Bills     if (bSigned)
15072867debSJason M. Bills     {
15172867debSJason M. Bills         return static_cast<int8_t>(scaledValue);
15272867debSJason M. Bills     }
15372867debSJason M. Bills     else
15472867debSJason M. Bills     {
15572867debSJason M. Bills         return static_cast<uint8_t>(scaledValue);
15672867debSJason M. Bills     }
15772867debSJason M. Bills }
15872867debSJason M. Bills 
15972867debSJason M. Bills static inline uint8_t getScaledIPMIValue(const double value, const double max,
16072867debSJason M. Bills                                          const double min)
16172867debSJason M. Bills {
16272867debSJason M. Bills     int16_t mValue = 0;
16372867debSJason M. Bills     int8_t rExp = 0;
16472867debSJason M. Bills     int16_t bValue = 0;
16572867debSJason M. Bills     int8_t bExp = 0;
16672867debSJason M. Bills     bool bSigned = 0;
16772867debSJason M. Bills     bool result = 0;
16872867debSJason M. Bills 
16972867debSJason M. Bills     result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
17072867debSJason M. Bills     if (!result)
17172867debSJason M. Bills     {
17239417c7bSJames Feist         throw std::runtime_error("Illegal sensor attributes");
17372867debSJason M. Bills     }
17472867debSJason M. Bills     return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned);
17572867debSJason M. Bills }
17672867debSJason M. Bills 
1773f7c5e40SJason M. Bills } // namespace ipmi
178