airmon_aqi.c 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. #include "airmon_aqi.h"
  2. #include <math.h>
  3. #define CLAMP(x, min, max) ((x > max) ? max : (x < min) ? min : x)
  4. #define LINTP(x, from_l, from_h, to_l, to_h) \
  5. (((x - (from_l)) / (from_h - (from_l))) * (to_h - (to_l)) + to_l)
  6. #define NUM_BREAKPOINTS 8
  7. static const float pm2_5_breakpoints[NUM_BREAKPOINTS] =
  8. {0.f, 12.1f, 35.5f, 55.5f, 150.5f, 250.5f, 350.5f, 500.f};
  9. static const float pm10_breakpoints[NUM_BREAKPOINTS] =
  10. {0.f, 55.f, 155.f, 255.f, 355.f, 425.f, 505.f, 605.f};
  11. static const float aqi_breakpoints[NUM_BREAKPOINTS] =
  12. {0.f, 51.f, 101.f, 151.f, 201.f, 301.f, 401.f, 500.f};
  13. const char* const aqi_levels[NUM_BREAKPOINTS] = {
  14. "Good",
  15. "Moderate",
  16. "Unhealthy for Sensitive",
  17. "Unhealthy",
  18. "Very Unhealthy",
  19. "Hazardous",
  20. "Hazardous",
  21. "Hazardous",
  22. };
  23. int airmon_aqi_breakpoint_idx(const float bps[], float value) {
  24. int i = 0;
  25. for(; i < NUM_BREAKPOINTS - 1; i++) {
  26. if(bps[i] <= value && value < bps[i + 1]) {
  27. break;
  28. }
  29. }
  30. return i;
  31. }
  32. int airmon_aqi_pm(float pm, const float bps[], float q) {
  33. float c = floorf(pm / q) * q;
  34. int i = airmon_aqi_breakpoint_idx(bps, c);
  35. float aqi = LINTP(c, bps[i], bps[i + 1] - q, aqi_breakpoints[i], aqi_breakpoints[i + 1] - 1);
  36. return (int)(CLAMP(aqi, aqi_breakpoints[0], aqi_breakpoints[NUM_BREAKPOINTS - 1]));
  37. }
  38. int airmon_aqi(float pm2_5, float pm10) {
  39. int aqi2_5 = airmon_aqi_pm(pm2_5, pm2_5_breakpoints, 0.1f);
  40. int aqi10 = airmon_aqi_pm(pm10, pm10_breakpoints, 1.0f);
  41. if(aqi10 > aqi2_5) {
  42. return aqi10;
  43. }
  44. return aqi2_5;
  45. }
  46. const char* airmon_aqi_level(int aqi) {
  47. return aqi_levels[airmon_aqi_breakpoint_idx(aqi_breakpoints, aqi)];
  48. }