class ObsData(base.ObsData): """SDS01x observations time measurement time [seconds since epoch] raw25, raw10 PM2.5*10, PM10*10 [ug/m3] """ pm25: float = field( metadata=base.metadata("PM2.5", "ug/m3", "concentration")) pm10: float = field( metadata=base.metadata("PM10", "ug/m3", "concentration")) def __post_init__(self): """Convert from 0.1 ug/m3 to ug/m3""" self.pm25 /= 10 self.pm10 /= 10 def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "pm": return f"{self.date:%F %T}: PM2.5 {self.pm25:.1f}, PM10 {self.pm10:.1f} ug/m3" if spec == "csv": return f"{self.time}, {self.pm25:.1f}, {self.pm10:.1f}" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(base.ObsData): """Observations from Honeywell HPMA115C0 sensors time measurement time [seconds since epoch] pm01, pm25, pm04, pm10 PM1.0, PM2.5, PM4.0 PM10 [ug/m3] """ pm01: int = field(metadata=base.metadata("PM1", "ug/m3", "concentration")) pm25: int = field( metadata=base.metadata("PM2.5", "ug/m3", "concentration")) pm04: int = field(metadata=base.metadata("PM4", "ug/m3", "concentration")) pm10: int = field(metadata=base.metadata("PM10", "ug/m3", "concentration")) def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "pm": return f"{self.date:%F %T}: PM1 {self.pm01:.1f}, PM2.5 {self.pm25:.1f}, PM4 {self.pm04:.1f}, PM10 {self.pm10:.1f} ug/m3" if spec == "csv": return ( f"{self.time}, {self.pm01:.1f}, {self.pm25:.1f}, {self.pm04:.1f}, {self.pm10:.1f}" ) raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(base.ObsData): """Observations from Plantower PMS3003 sensors time measurement time [seconds since epoch] raw01, raw25, raw10 cf=1 PM estimates [ug/m3] pm01, pm25, pm10 PM1.0, PM2.5, PM10 [ug/m3] """ raw01: int = field(repr=False) raw25: int = field(repr=False) raw10: int = field(repr=False) pm01: int = field(metadata=base.metadata("PM1", "ug/m3", "concentration")) pm25: int = field( metadata=base.metadata("PM2.5", "ug/m3", "concentration")) pm10: int = field(metadata=base.metadata("PM10", "ug/m3", "concentration")) # cfX [1]: pmX/rawX @property def cf01(self) -> float: return self._safe_div(self.pm01, self.raw01) @property def cf25(self) -> float: return self._safe_div(self.pm25, self.raw25) @property def cf10(self) -> float: return self._safe_div(self.pm10, self.raw10) @staticmethod def _safe_div(x: int, y: int) -> float: if y: return x / y if x == y == 0: # pragma: no cover return 1 return 0 # pragma: no cover def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "pm": return f"{self.date:%F %T}: PM1 {self.pm01:.1f}, PM2.5 {self.pm25:.1f}, PM10 {self.pm10:.1f} ug/m3" if spec == "csv": return f"{self.time}, {self.raw01}, {self.raw25}, {self.raw10}, {self.pm01:.1f}, {self.pm25:.1f}, {self.pm10:.1f}" if spec == "cf": return f"{self.date:%F %T}: CF1 {self.cf01:.0%}, CF2.5 {self.cf25:.0%}, CF10 {self.cf10:.0%}" if spec == "raw": return ( f"{self.date:%F %T}: PM1 {self.raw01}, PM2.5 {self.raw25}, PM10 {self.raw10} ug/m3" ) raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(pms3003.ObsData): """Observations from Plantower PMS5003T sensors time measurement time [seconds since epoch] raw01, raw25, raw10 cf=1 PM estimates [ug/m3] pm01, pm25, pm10 PM1.0, PM2.5, PM10 [ug/m3] n0_3, n0_5, n1_0, n2_5 number concentrations over X.Y um [#/cm3] temp temperature [°C] rhum relative humidity [%] """ # nX_Y [#/cm3]: number concentrations over X.Y um (read as 100*nX_Y) n0_3: float n0_5: float n1_0: float n2_5: float # temp[°C],rhum[%]: temperature,relative humidity (read as 10*temp,10*rhum) temp: float = field(metadata=base.metadata("temperature", "°C", "degrees")) rhum: float = field( metadata=base.metadata("relative humidity", "%", "percentage")) def __post_init__(self): """Units conversion nX_Y [#/cm3] read in [#/0.1L] temp [°C] read in [0.1 °C] rhum [%] read in [1/1000] """ self.n0_3 /= 100 self.n0_5 /= 100 self.n1_0 /= 100 self.n2_5 /= 100 self.temp /= 10 self.rhum /= 10 if self.n0_3 == 0 and self.pm10 > 0: raise InconsistentObservation( f"inconsistent obs: PM10={self.pm10} and N0.3={self.n0_3}") def __format__(self, spec: str) -> str: if spec in ["header", "pm", "raw", "cf"]: return super().__format__(spec) if spec == "csv": pm = super().__format__(spec) return f"{pm}, {self.n0_3:.2f}, {self.n0_5:.2f}, {self.n1_0:.2f}, {self.n2_5:.2f}, {self.temp:.1f}, {self.rhum:.1f}" if spec == "num": return f"{self.date:%F %T}: N0.3 {self.n0_3:.2f}, N0.5 {self.n0_5:.2f}, N1.0 {self.n1_0:.2f}, N2.5 {self.n2_5:.2f} #/cm3" if spec == "atm": return f"{self.date:%F %T}: Temp. {self.temp:.1f} °C, Rel.Hum. {self.rhum:.1f} %" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(pmsx003.ObsData): """Observations from Plantower PMS5003S sensors time measurement time [seconds since epoch] raw01, raw25, raw10 cf=1 PM estimates [ug/m3] pm01, pm25, pm10 PM1.0, PM2.5, PM10 [ug/m3] n0_3, n0_5, n1_0, n2_5, n5_0, n10_0 number concentrations over X.Y um [#/cm3] HCHO formaldehyde concentration [mg/m3] """ # HCHO [mg/m3]: formaldehyde concentration (read as ug/m3, datasheet says 1/1000 mg/m3 ie ug/m3) HCHO: int = field( metadata=base.metadata("formaldehyde", "mg/m3", "concentration")) def __post_init__(self): """Units conversion nX_Y [#/cm3] read in [#/0.1L] HCHO [mg/m3] read in [ug/m3] """ super().__post_init__() self.HCHO /= 1000 def __format__(self, spec: str) -> str: if spec in ["header", "pm", "raw", "cf", "num"]: return super().__format__(spec) if spec == "csv": csv = super().__format__(spec) return f"{csv}, {self.HCHO:.3f}" if spec == "hcho": return f"{self.date:%F %T}: HCHO {self.HCHO:.3f} mg/m3" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(base.ObsData): """SPS30 observations time measurement time [seconds since epoch] pm01, pm25, pm04, pm10 PM1.0, PM2.5, PM4.0, PM10 [ug/m3] n0_5, n1_0, n2_5, n4_0, n10_0 number concentrations between 0.3 and X.Y um [#/cm3] diam typical particle size [μm] """ pm01: float = field( metadata=base.metadata("PM1", "ug/m3", "concentration")) pm25: float = field( metadata=base.metadata("PM2.5", "ug/m3", "concentration")) pm04: float = field( metadata=base.metadata("PM4", "ug/m3", "concentration")) pm10: float = field( metadata=base.metadata("PM10", "ug/m3", "concentration")) n0_5: float n1_0: float n2_5: float n4_0: float n10_0: float diam: float def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "pm": return f"{self.date:%F %T}: PM1 {self.pm01:.1f}, PM2.5 {self.pm25:.1f}, PM4 {self.pm04:.1f}, PM10 {self.pm10:.1f} ug/m3" if spec == "csv": pm = f"{self.pm01:.1f}, {self.pm25:.1f}, {self.pm04:.1f}, {self.pm10:.1f}" num = f"{self.n0_5:.2f}, {self.n1_0:.2f}, {self.n2_5:.2f}, {self.n4_0:.2f}, {self.n10_0:.2f}" return f"{self.time}, {pm}, {num}, {self.diam:.1f}" if spec == "num": return f"{self.date:%F %T}: N0.5 {self.n0_5:.2f}, N1.0 {self.n1_0:.2f}, N2.5 {self.n2_5:.2f}, N4.0 {self.n4_0:.2f}, N10 {self.n10_0:.2f} #/cm3" if spec == "diam": return f"{self.date:%F %T}: Ø {self.diam:.1f} μm" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(pms5003s.ObsData): """Observations from Plantower PMS5003ST sensors time measurement time [seconds since epoch] raw01, raw25, raw10 cf=1 PM estimates [ug/m3] pm01, pm25, pm10 PM1.0, PM2.5, PM10 [ug/m3] n0_3, n0_5, n1_0, n2_5, n5_0, n10_0 number concentrations over X.Y um [#/cm3] HCHO formaldehyde concentration [mg/m3] temp temperature [°C] rhum relative humidity [%] """ # temp[°C],rhum[%]: temperature,relative humidity (read as 10*temp,10*rhum) temp: float = field(metadata=base.metadata("temperature", "°C", "degrees")) rhum: float = field(metadata=base.metadata("relative humidity", "%", "percentage")) def __post_init__(self): """Units conversion nX_Y [#/cm3] read in [#/0.1L] HCHO [mg/m3] read in [ug/m3] temp [°C] read in [0.1 °C] rhum [%] read in [1/1000] """ super().__post_init__() self.temp /= 10 self.rhum /= 10 def __format__(self, spec: str) -> str: if spec in ["header", "pm", "raw", "cf", "num", "hcho"]: return super().__format__(spec) if spec == "csv": csv = super().__format__(spec) return f"{csv}, {self.temp:.1f}, {self.rhum:.1f}" if spec == "atm": return f"{self.date:%F %T}: Temp. {self.temp:.1f} °C, Rel.Hum. {self.rhum:.1f} %" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'" )
class ObsData(base.ObsData): """SDS198 observations time measurement time [seconds since epoch] pm100 PM100 [ug/m3] """ pm100: int = field( metadata=base.metadata("PM100", "ug/m3", "concentration")) def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "pm": return f"{self.date:%F %T}: PM100 {self.pm100:.1f} ug/m3" if spec == "csv": return f"{self.time}, {self.pm100:.1f}" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'")
class ObsData(base.ObsData): """Observations from Plantower PMS3003 sensors time measurement time [seconds since epoch] temp temperature [°C] rhum relative humidity [%] press atmospheric pressure [hPa] IAQ_acc IAQ accuracy flag IAQ index of air quality [0--500] gas gas resistance [kΩ] alt altitude estimate [m a.s.l.] """ # temp[°C],rhum[%]: temperature,relative humidity (read as 100*temp,100*rhum) temp: float = field(metadata=base.metadata("temperature", "°C", "degrees")) rhum: float = field( metadata=base.metadata("relative humidity", "%", "percentage")) # press[hPa]: atm. pressure (read as 24b in hPa) # on read press XSB(8b)|MSB(8b) pres: float = field( metadata=base.metadata("atmospheric pressure", "hPa", "pressure")) # on read press LSB(8b) IAQ_acc: int = field(metadata=base.metadata("IAQ acc", "1", "acc")) # on read IAQ_acc(4b)|IAQ(12b) packed into 16b IAQ: int = field(metadata=base.metadata("IAQ", "0-500", "iaq")) gas: int = field( metadata=base.metadata("gas resistance", "kΩ", "resistance")) alt: int = field( metadata=base.metadata("altitude estimate", "m(a.s.l.)", "elevation")) def __post_init__(self): """Units conversion temp [°C] read in [0.01 °C] rhum [%] read in [1/10 000] press [hPa] read in [Pa] across 12b gas [kΩ] read in [Ω] """ self.temp /= 100 self.rhum /= 100 self.press = (int(self.pres) << 8 | self.IAQ_acc) / 100 self.IAQ_acc = self.IAQ >> 4 self.IAQ &= 0x0FFF self.gas /= 1000 def __format__(self, spec: str) -> str: if spec == "header": return super().__format__(spec) if spec == "atm": return f"{self.date:%F %T}: Temp. {self.temp:.1f} °C, Rel.Hum. {self.rhum:.1f} %, Press {self.press:.2f} hPa" if spec == "bme": return f"{self.date:%F %T}: Temp. {self.temp:.1f} °C, Rel.Hum. {self.rhum:.1f} %, Press {self.press:.2f} hPa, {self.gas:.1f} kΩ" if spec == "bsec": return f"{self.date:%F %T}: Temp. {self.temp:.1f} °C, Rel.Hum. {self.rhum:.1f} %, Press {self.press:.2f} hPa, {self.IAQ} IAQ" if spec == "csv": return f"{self.time}, {self.temp:.1f}, {self.rhum:.1f}, {self.press:.2f}, {self.IAQ_acc}, {self.IAQ}, {self.gas:.1f}, {self.alt}" raise ValueError( # pragma: no cover f"Unknown format code '{spec}' " f"for object of type '{__name__}.{self.__class__.__name__}'") def __str__(self): return self.__format__("bme")