polynomial.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Generic polynomial calculation using integer coefficients.
  4. *
  5. * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
  6. *
  7. * Authors:
  8. * Maxim Kaurkin <[email protected]>
  9. * Serge Semin <[email protected]>
  10. *
  11. */
  12. #include <linux/kernel.h>
  13. #include <linux/module.h>
  14. #include <linux/polynomial.h>
  15. /*
  16. * Originally this was part of drivers/hwmon/bt1-pvt.c.
  17. * There the following conversion is used and should serve as an example here:
  18. *
  19. * The original translation formulae of the temperature (in degrees of Celsius)
  20. * to PVT data and vice-versa are following:
  21. *
  22. * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
  23. * 1.7204e2
  24. * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
  25. * 3.1020e-1*(N^1) - 4.838e1
  26. *
  27. * where T = [-48.380, 147.438]C and N = [0, 1023].
  28. *
  29. * They must be accordingly altered to be suitable for the integer arithmetics.
  30. * The technique is called 'factor redistribution', which just makes sure the
  31. * multiplications and divisions are made so to have a result of the operations
  32. * within the integer numbers limit. In addition we need to translate the
  33. * formulae to accept millidegrees of Celsius. Here what they look like after
  34. * the alterations:
  35. *
  36. * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
  37. * 17204e2) / 1e4
  38. * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
  39. * 48380
  40. * where T = [-48380, 147438] mC and N = [0, 1023].
  41. *
  42. * static const struct polynomial poly_temp_to_N = {
  43. * .total_divider = 10000,
  44. * .terms = {
  45. * {4, 18322, 10000, 10000},
  46. * {3, 2343, 10000, 10},
  47. * {2, 87018, 10000, 10},
  48. * {1, 39269, 1000, 1},
  49. * {0, 1720400, 1, 1}
  50. * }
  51. * };
  52. *
  53. * static const struct polynomial poly_N_to_temp = {
  54. * .total_divider = 1,
  55. * .terms = {
  56. * {4, -16743, 1000, 1},
  57. * {3, 81542, 1000, 1},
  58. * {2, -182010, 1000, 1},
  59. * {1, 310200, 1000, 1},
  60. * {0, -48380, 1, 1}
  61. * }
  62. * };
  63. */
  64. /**
  65. * polynomial_calc - calculate a polynomial using integer arithmetic
  66. *
  67. * @poly: pointer to the descriptor of the polynomial
  68. * @data: input value of the polynimal
  69. *
  70. * Calculate the result of a polynomial using only integer arithmetic. For
  71. * this to work without too much loss of precision the coefficients has to
  72. * be altered. This is called factor redistribution.
  73. *
  74. * Returns the result of the polynomial calculation.
  75. */
  76. long polynomial_calc(const struct polynomial *poly, long data)
  77. {
  78. const struct polynomial_term *term = poly->terms;
  79. long total_divider = poly->total_divider ?: 1;
  80. long tmp, ret = 0;
  81. int deg;
  82. /*
  83. * Here is the polynomial calculation function, which performs the
  84. * redistributed terms calculations. It's pretty straightforward.
  85. * We walk over each degree term up to the free one, and perform
  86. * the redistributed multiplication of the term coefficient, its
  87. * divider (as for the rationale fraction representation), data
  88. * power and the rational fraction divider leftover. Then all of
  89. * this is collected in a total sum variable, which value is
  90. * normalized by the total divider before being returned.
  91. */
  92. do {
  93. tmp = term->coef;
  94. for (deg = 0; deg < term->deg; ++deg)
  95. tmp = mult_frac(tmp, data, term->divider);
  96. ret += tmp / term->divider_leftover;
  97. } while ((term++)->deg);
  98. return ret / total_divider;
  99. }
  100. EXPORT_SYMBOL_GPL(polynomial_calc);
  101. MODULE_DESCRIPTION("Generic polynomial calculations");
  102. MODULE_LICENSE("GPL");