diff --git a/src/ppd42ns/ppd42ns.c b/src/ppd42ns/ppd42ns.c index f75a688b..21316f65 100644 --- a/src/ppd42ns/ppd42ns.c +++ b/src/ppd42ns/ppd42ns.c @@ -1,5 +1,7 @@ /* * Author: Jon Trulson + * Contributions: Rex Tsai + * Abhishek Malik * Copyright (c) 2016 Intel Corporation. * * Rewritten Based on original C++ driver written by: @@ -39,6 +41,7 @@ // from LOW to HIGH static uint32_t ppd42ns_pulse_in(const ppd42ns_context dev, bool high_low_value); +double pcs2ugm3 (double concentration_pcs); ppd42ns_context ppd42ns_init(int pin) { @@ -112,6 +115,8 @@ ppd42ns_dust_data ppd42ns_get_data(const ppd42ns_context dev) data.lowPulseOccupancy = low_pulse_occupancy; data.ratio = ratio; data.concentration = concentration; + data.ugm3 = pcs2ugm3(data.concentration); + data.aqi = upm_ugm3_to_aqi(data.ugm3); return data; } @@ -165,3 +170,26 @@ static uint32_t ppd42ns_pulse_in(const ppd42ns_context dev, return total_pulse_time; } + +// Assumes density, shape, and size of dust to estimate mass concentration from particle count. +// +// This method was described in a 2009 paper +// Preliminary Screening System for Ambient Air Quality in Southeast Philadelphia by Uva, M., Falcone, R., McClellan, A., and Ostapowicz, E. +// https://www.yumpu.com/en/document/view/31692906/preliminary-screening-system-for-ambient-air-quality-in-southeast- +// +// This method does not use the correction factors, based on the presence of humidity and rain in the paper. +// +// convert from particles/0.01 ft3 to µg/m3 +double pcs2ugm3 (double concentration_pcs) +{ + double pi = 3.14159; + // All particles are spherical, with a density of 1.65E12 µg/m3 + double density = 1.65 * pow (10, 12); + // The radius of a particle in the PM2.5 channel is .44 µm + double r25 = 0.44 * pow (10, -6); + double vol25 = (4/3) * pi * pow (r25, 3); + double mass25 = density * vol25; // ug + double K = 3531.5; // per m^3 + + return concentration_pcs * K * mass25; +} diff --git a/src/ppd42ns/ppd42ns_data.h b/src/ppd42ns/ppd42ns_data.h index 1b65e7f9..d8b45675 100644 --- a/src/ppd42ns/ppd42ns_data.h +++ b/src/ppd42ns/ppd42ns_data.h @@ -1,5 +1,7 @@ /* * Author: Jon Trulson + * Contributions: Rex Tsai + * Abhishek Malik * Copyright (c) 2016 Intel Corporation. * * Based on original C++ driver written by: @@ -37,6 +39,8 @@ extern "C" { unsigned int lowPulseOccupancy; double ratio; double concentration; + double ugm3; + int aqi; } ppd42ns_dust_data; #ifdef __cplusplus diff --git a/src/utilities/upm_utilities.c b/src/utilities/upm_utilities.c index 2cce14e2..952394ef 100644 --- a/src/utilities/upm_utilities.c +++ b/src/utilities/upm_utilities.c @@ -1,7 +1,9 @@ /* * Authors: * Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Contributions: Rex Tsai + * Abhishek Malik + * Copyright (c) 2017 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,6 +34,22 @@ #include #include +// https://airnow.gov/index.cfm?action=aqibasics.aqi +static struct aqi { + float clow; + float chigh; + int llow; + int lhigh; +} aqi[] = { + {0.0, 12.4, 0, 50}, + {12.1, 35.4, 51, 100}, + {35.5, 55.4, 101, 150}, + {55.5, 150.4, 151, 200}, + {150.5, 250.4, 201, 300}, + {250.5, 350.4, 301, 350}, + {350.5, 500.4, 401, 500}, +}; + void upm_delay(unsigned int time) { if (time <= 0) @@ -246,3 +264,19 @@ uint32_t upm_elapsed_us(upm_clock_t *clock) return elapsed; #endif } + +int upm_ugm3_to_aqi (double ugm3) +{ + int i; + + for (i = 0; i < 7; i++) { + if (ugm3 >= aqi[i].clow && + ugm3 <= aqi[i].chigh) { + // Ip = [(Ihi-Ilow)/(BPhi-BPlow)] (Cp-BPlow)+Ilow, + return ((aqi[i].lhigh - aqi[i].llow) / (aqi[i].chigh - aqi[i].clow)) * + (ugm3 - aqi[i].clow) + aqi[i].llow; + } + } + + return 0; +} diff --git a/src/utilities/upm_utilities.h b/src/utilities/upm_utilities.h index a93b3765..3bb39bd4 100644 --- a/src/utilities/upm_utilities.h +++ b/src/utilities/upm_utilities.h @@ -1,7 +1,9 @@ /* * Authors: * Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Contributions: Rex Tsai + * Abhishek Malik + * Copyright (c) 2017 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -113,6 +115,15 @@ uint32_t upm_elapsed_ms(upm_clock_t *clock); */ uint32_t upm_elapsed_us(upm_clock_t *clock); +/** + * Return the AQI (based on EPA standards) using the ugm3 value + * calculated by the sensor module. + * + * @param ugm3 micrograms per cubic meter + * @return calculated AQI + */ +int upm_ugm3_to_aqi (double ugm3); + #ifdef __cplusplus } #endif