1use crate::bme::{BmeSensor, BmeSettingsHandle};
169use crate::clock::Clock;
170use crate::error::{BsecError, ConversionError, Error};
171#[cfg(not(feature = "docs-rs"))]
172use libalgobsec_sys::{
173 bsec_bme_settings_t, bsec_do_steps, bsec_get_configuration, bsec_get_state, bsec_get_version,
174 bsec_init, bsec_input_t, bsec_library_return_t, bsec_output_t, bsec_physical_sensor_t,
175 bsec_reset_output, bsec_sensor_configuration_t, bsec_sensor_control, bsec_set_configuration,
176 bsec_set_state, bsec_update_subscription, bsec_version_t, bsec_virtual_sensor_t,
177 BSEC_MAX_PHYSICAL_SENSOR, BSEC_MAX_PROPERTY_BLOB_SIZE, BSEC_MAX_STATE_BLOB_SIZE,
178 BSEC_MAX_WORKBUFFER_SIZE,
179};
180use std::borrow::Borrow;
181use std::convert::{From, TryFrom, TryInto};
182use std::fmt::Debug;
183use std::hash::Hash;
184use std::marker::PhantomData;
185use std::sync::atomic::{AtomicBool, Ordering};
186use std::time::Duration;
187
188#[cfg(feature = "docs-rs")]
189#[allow(non_camel_case_types)]
190struct bsec_library_return_t {}
191
192#[cfg(feature = "docs-rs")]
193#[allow(non_camel_case_types)]
194struct bsec_output_t {}
195
196#[cfg(feature = "docs-rs")]
197#[allow(non_camel_case_types)]
198struct bsec_physical_sensor_t {}
199
200#[cfg(feature = "docs-rs")]
201#[allow(non_camel_case_types)]
202struct bsec_sensor_configuration_t {}
203
204#[cfg(feature = "docs-rs")]
205#[allow(non_camel_case_types)]
206struct bsec_virtual_sensor_t {}
207
208pub mod bme;
209pub mod clock;
210pub mod error;
211
212static BSEC_IN_USE: AtomicBool = AtomicBool::new(false);
213
214pub struct Bsec<S: BmeSensor, C: Clock, B: Borrow<C>> {
216 bme: S,
217 subscribed: u32,
218 ulp_plus_queue: u32,
219 next_measurement: i64,
220 clock: B,
221 _clock_type: PhantomData<C>,
222}
223
224impl<S: BmeSensor, C: Clock, B: Borrow<C>> Bsec<S, C, B> {
225 pub fn init(bme: S, clock: B) -> Result<Self, Error<S::Error>> {
231 if BSEC_IN_USE
232 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
233 .is_ok()
234 {
235 unsafe {
236 bsec_init().into_result()?;
237 }
238 Ok(Self {
239 bme,
240 subscribed: 0,
241 ulp_plus_queue: 0,
242 next_measurement: clock.borrow().timestamp_ns(),
243 clock,
244 _clock_type: PhantomData,
245 })
246 } else {
247 Err(Error::BsecAlreadyInUse)
248 }
249 }
250
251 pub fn update_subscription(
259 &mut self,
260 requests: &[SubscriptionRequest],
261 ) -> Result<Vec<RequiredInput>, Error<S::Error>> {
262 let bsec_requested_outputs: Vec<bsec_sensor_configuration_t> =
263 requests.iter().map(From::from).collect();
264 let mut required_sensor_settings = [bsec_sensor_configuration_t {
265 sample_rate: 0.,
266 sensor_id: 0,
267 }; BSEC_MAX_PHYSICAL_SENSOR as usize];
268 let mut n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR as u8;
269 unsafe {
270 bsec_update_subscription(
271 bsec_requested_outputs.as_ptr(),
272 requests
273 .len()
274 .try_into()
275 .or(Err(Error::ArgumentListTooLong))?,
276 required_sensor_settings.as_mut_ptr(),
277 &mut n_required_sensor_settings,
278 )
279 .into_result()?
280 }
281 for changed in requests.iter() {
282 match changed.sample_rate {
283 SampleRate::Disabled => {
284 self.subscribed &= !(changed.sensor as u32);
285 self.ulp_plus_queue &= !(changed.sensor as u32);
286 }
287 SampleRate::UlpMeasurementOnDemand => {
288 self.ulp_plus_queue |= changed.sensor as u32;
289 }
290 _ => {
291 self.subscribed |= changed.sensor as u32;
292 }
293 }
294 }
295 Ok(required_sensor_settings
296 .iter()
297 .take(n_required_sensor_settings as usize)
298 .map(RequiredInput::from)
299 .collect())
300 }
301
302 pub fn next_measurement(&self) -> i64 {
304 self.next_measurement
305 }
306
307 pub fn start_next_measurement(&mut self) -> nb::Result<Duration, Error<S::Error>> {
312 let mut bme_settings = bsec_bme_settings_t {
313 next_call: 0,
314 process_data: 0,
315 heater_temperature: 0,
316 heating_duration: 0,
317 run_gas: 0,
318 pressure_oversampling: 0,
319 temperature_oversampling: 0,
320 humidity_oversampling: 0,
321 trigger_measurement: 0,
322 };
323 unsafe {
324 bsec_sensor_control(self.clock.borrow().timestamp_ns(), &mut bme_settings)
325 .into_result()
326 .map_err(Error::BsecError)?;
327 }
328 self.next_measurement = bme_settings.next_call;
329 if bme_settings.trigger_measurement != 1 {
330 return Err(nb::Error::WouldBlock);
331 }
332 self.bme
333 .start_measurement(&BmeSettingsHandle::new(&bme_settings))
334 .map_err(Error::BmeSensorError)
335 .map_err(nb::Error::Other)
336 }
337
338 pub fn process_last_measurement(&mut self) -> nb::Result<Vec<Output>, Error<S::Error>> {
346 let time_stamp = self.clock.borrow().timestamp_ns();
347 let inputs: Vec<bsec_input_t> = self
348 .bme
349 .get_measurement()
350 .map_err(|e| e.map(Error::BmeSensorError))?
351 .iter()
352 .map(|o| bsec_input_t {
353 time_stamp,
354 signal: o.signal,
355 signal_dimensions: 1,
356 sensor_id: o.sensor.into(),
357 })
358 .collect();
359 let mut outputs = vec![
360 bsec_output_t {
361 time_stamp: 0,
362 signal: 0.,
363 signal_dimensions: 1,
364 sensor_id: 0,
365 accuracy: 0,
366 };
367 (self.subscribed | self.ulp_plus_queue).count_ones() as usize
368 ];
369 let mut num_outputs: u8 = outputs
370 .len()
371 .try_into()
372 .or(Err(Error::ArgumentListTooLong))?;
373 self.ulp_plus_queue = 0;
374 unsafe {
375 bsec_do_steps(
376 inputs.as_ptr(),
377 inputs
378 .len()
379 .try_into()
380 .or(Err(Error::ArgumentListTooLong))?,
381 outputs.as_mut_ptr(),
382 &mut num_outputs,
383 )
384 .into_result()
385 .map_err(Error::BsecError)?;
386 }
387
388 let signals: Result<Vec<Output>, Error<S::Error>> = outputs
389 .iter()
390 .take(num_outputs.into())
391 .map(|x| Output::try_from(x).map_err(Error::<S::Error>::from))
392 .collect();
393 Ok(signals?)
394 }
395
396 pub fn get_state(&self) -> Result<Vec<u8>, Error<S::Error>> {
399 let mut state = [0u8; BSEC_MAX_STATE_BLOB_SIZE as usize];
400 let mut work_buffer = [0u8; BSEC_MAX_WORKBUFFER_SIZE as usize];
401 let mut state_length = BSEC_MAX_STATE_BLOB_SIZE;
402 unsafe {
403 bsec_get_state(
404 0,
405 state.as_mut_ptr(),
406 state.len() as u32,
407 work_buffer.as_mut_ptr(),
408 work_buffer.len() as u32,
409 &mut state_length,
410 )
411 .into_result()?;
412 }
413 Ok(state[..state_length as usize].into())
414 }
415
416 pub fn set_state(&mut self, state: &[u8]) -> Result<(), Error<S::Error>> {
419 let mut work_buffer = [0u8; BSEC_MAX_WORKBUFFER_SIZE as usize];
420 unsafe {
421 bsec_set_state(
422 state.as_ptr(),
423 state.len() as u32,
424 work_buffer.as_mut_ptr(),
425 work_buffer.len() as u32,
426 )
427 .into_result()?;
428 }
429 Ok(())
430 }
431
432 pub fn get_configuration(&self) -> Result<Vec<u8>, Error<S::Error>> {
434 let mut serialized_settings = [0u8; BSEC_MAX_PROPERTY_BLOB_SIZE as usize];
435 let mut serialized_settings_length = 0u32;
436 let mut work_buffer = [0u8; BSEC_MAX_WORKBUFFER_SIZE as usize];
437 unsafe {
438 bsec_get_configuration(
439 0,
440 serialized_settings.as_mut_ptr(),
441 serialized_settings.len() as u32,
442 work_buffer.as_mut_ptr(),
443 work_buffer.len() as u32,
444 &mut serialized_settings_length,
445 )
446 .into_result()?;
447 }
448 Ok(serialized_settings[..serialized_settings_length as usize].into())
449 }
450
451 pub fn set_configuration(&mut self, serialized_settings: &[u8]) -> Result<(), Error<S::Error>> {
457 let mut work_buffer = [0u8; BSEC_MAX_WORKBUFFER_SIZE as usize];
458 unsafe {
459 bsec_set_configuration(
460 serialized_settings.as_ptr(),
461 serialized_settings.len() as u32,
462 work_buffer.as_mut_ptr(),
463 work_buffer.len() as u32,
464 )
465 .into_result()?
466 }
467 Ok(())
468 }
469
470 pub fn reset_output(&mut self, sensor: OutputKind) -> Result<(), Error<S::Error>> {
473 unsafe {
474 bsec_reset_output(bsec_virtual_sensor_t::from(sensor) as u8).into_result()?;
475 }
476 Ok(())
477 }
478}
479
480impl<S: BmeSensor, C: Clock, B: Borrow<C>> Drop for Bsec<S, C, B> {
481 fn drop(&mut self) {
482 BSEC_IN_USE.store(false, Ordering::Release);
483 }
484}
485
486pub fn get_version() -> Result<(u8, u8, u8, u8), BsecError> {
491 let mut version = bsec_version_t {
492 major: 0,
493 minor: 0,
494 major_bugfix: 0,
495 minor_bugfix: 0,
496 };
497 unsafe {
498 bsec_get_version(&mut version).into_result()?;
499 }
500 Ok((
501 version.major,
502 version.minor,
503 version.major_bugfix,
504 version.minor_bugfix,
505 ))
506}
507
508#[derive(Clone, Copy, Debug, PartialEq)]
510pub struct Input {
511 pub signal: f32,
513
514 pub sensor: InputKind,
516}
517
518#[derive(Clone, Copy, Debug, PartialEq)]
520pub struct Output {
521 pub timestamp_ns: i64,
525
526 pub signal: f64,
528
529 pub sensor: OutputKind,
531
532 pub accuracy: Accuracy,
534}
535
536impl TryFrom<&bsec_output_t> for Output {
537 type Error = ConversionError;
538 fn try_from(output: &bsec_output_t) -> Result<Self, ConversionError> {
539 Ok(Self {
540 timestamp_ns: output.time_stamp,
541 signal: output.signal.into(),
542 sensor: output.sensor_id.try_into()?,
543 accuracy: output.accuracy.try_into()?,
544 })
545 }
546}
547
548#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
550pub enum Accuracy {
551 Unreliable = 0,
552 LowAccuracy = 1,
553 MediumAccuracy = 2,
554 HighAccuracy = 3,
555}
556
557impl TryFrom<u8> for Accuracy {
558 type Error = ConversionError;
559 fn try_from(accuracy: u8) -> Result<Self, ConversionError> {
560 use Accuracy::*;
561 match accuracy {
562 0 => Ok(Unreliable),
563 1 => Ok(LowAccuracy),
564 2 => Ok(MediumAccuracy),
565 3 => Ok(HighAccuracy),
566 _ => Err(ConversionError::InvalidAccuracy(accuracy)),
567 }
568 }
569}
570
571#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
573pub struct SubscriptionRequest {
574 pub sample_rate: SampleRate,
576 pub sensor: OutputKind,
578}
579
580impl From<&SubscriptionRequest> for bsec_sensor_configuration_t {
581 fn from(sensor_configuration: &SubscriptionRequest) -> Self {
582 Self {
583 sample_rate: sensor_configuration.sample_rate.into(),
584 sensor_id: bsec_virtual_sensor_t::from(sensor_configuration.sensor) as u8,
585 }
586 }
587}
588
589#[derive(Clone, Copy, Debug, PartialEq)]
591pub struct RequiredInput {
592 pub sample_rate: f32,
594 pub sensor: InputKind,
596}
597
598impl From<&bsec_sensor_configuration_t> for RequiredInput {
599 fn from(sensor_configuration: &bsec_sensor_configuration_t) -> Self {
600 Self {
601 sample_rate: sensor_configuration.sample_rate,
602 sensor: InputKind::from(sensor_configuration.sensor_id),
603 }
604 }
605}
606
607#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
609pub enum SampleRate {
610 Disabled,
612 Ulp,
614 Continuous,
616 Lp,
618 UlpMeasurementOnDemand,
622}
623
624impl From<SampleRate> for f32 {
625 fn from(sample_rate: SampleRate) -> Self {
626 f64::from(sample_rate) as f32
627 }
628}
629
630impl From<SampleRate> for f64 {
631 fn from(sample_rate: SampleRate) -> Self {
632 use libalgobsec_sys::*;
633 use SampleRate::*;
634 match sample_rate {
635 Disabled => BSEC_SAMPLE_RATE_DISABLED,
636 Ulp => BSEC_SAMPLE_RATE_ULP,
637 Continuous => BSEC_SAMPLE_RATE_CONT,
638 Lp => BSEC_SAMPLE_RATE_LP,
639 UlpMeasurementOnDemand => BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND,
640 }
641 }
642}
643
644#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
646pub enum InputKind {
647 Pressure,
649 Humidity,
651 Temperature,
653 GasResistor,
655 HeatSource,
657 DisableBaselineTracker,
659 Other(u32),
661}
662
663impl From<u8> for InputKind {
664 fn from(physical_sensor: u8) -> Self {
665 Self::from(physical_sensor as u32)
666 }
667}
668
669impl From<u32> for InputKind {
670 fn from(physical_sensor: u32) -> Self {
671 #![allow(non_upper_case_globals)]
672 use libalgobsec_sys::*;
673 use InputKind::*;
674 match physical_sensor {
675 bsec_physical_sensor_t_BSEC_INPUT_PRESSURE => Pressure,
676 bsec_physical_sensor_t_BSEC_INPUT_HUMIDITY => Humidity,
677 bsec_physical_sensor_t_BSEC_INPUT_TEMPERATURE => Temperature,
678 bsec_physical_sensor_t_BSEC_INPUT_GASRESISTOR => GasResistor,
679 bsec_physical_sensor_t_BSEC_INPUT_HEATSOURCE => HeatSource,
680 bsec_physical_sensor_t_BSEC_INPUT_DISABLE_BASELINE_TRACKER => DisableBaselineTracker,
681 physical_sensor => Other(physical_sensor),
682 }
683 }
684}
685
686impl From<InputKind> for bsec_physical_sensor_t {
687 fn from(physical_sensor: InputKind) -> Self {
688 use libalgobsec_sys::*;
689 use InputKind::*;
690 match physical_sensor {
691 Pressure => bsec_physical_sensor_t_BSEC_INPUT_PRESSURE,
692 Humidity => bsec_physical_sensor_t_BSEC_INPUT_HUMIDITY,
693 Temperature => bsec_physical_sensor_t_BSEC_INPUT_TEMPERATURE,
694 GasResistor => bsec_physical_sensor_t_BSEC_INPUT_GASRESISTOR,
695 HeatSource => bsec_physical_sensor_t_BSEC_INPUT_HEATSOURCE,
696 DisableBaselineTracker => bsec_physical_sensor_t_BSEC_INPUT_DISABLE_BASELINE_TRACKER,
697 Other(sensor) => sensor,
698 }
699 }
700}
701
702impl From<InputKind> for u8 {
703 fn from(physical_sensor: InputKind) -> Self {
704 bsec_physical_sensor_t::from(physical_sensor) as Self
705 }
706}
707
708#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
712pub enum OutputKind {
713 Iaq = 0x0001,
714 StaticIaq = 0x0002,
715 Co2Equivalent = 0x0004,
716 BreathVocEquivalent = 0x0008,
717 RawTemperature = 0x0010,
718 RawPressure = 0x0020,
719 RawHumidity = 0x0040,
720 RawGas = 0x0080,
721 StabilizationStatus = 0x0100,
722 RunInStatus = 0x0200,
723 SensorHeatCompensatedTemperature = 0x0400,
724 SensorHeatCompensatedHumidity = 0x0800,
725 GasPercentage = 0x2000,
726}
727
728impl From<OutputKind> for bsec_virtual_sensor_t {
729 fn from(virtual_sensor: OutputKind) -> Self {
730 use libalgobsec_sys::*;
731 use OutputKind::*;
732 match virtual_sensor {
733 Iaq => bsec_virtual_sensor_t_BSEC_OUTPUT_IAQ,
734 StaticIaq => bsec_virtual_sensor_t_BSEC_OUTPUT_STATIC_IAQ,
735 Co2Equivalent => bsec_virtual_sensor_t_BSEC_OUTPUT_CO2_EQUIVALENT,
736 BreathVocEquivalent => bsec_virtual_sensor_t_BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
737 RawTemperature => bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_TEMPERATURE,
738 RawPressure => bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_PRESSURE,
739 RawHumidity => bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_HUMIDITY,
740 RawGas => bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_GAS,
741 StabilizationStatus => bsec_virtual_sensor_t_BSEC_OUTPUT_STABILIZATION_STATUS,
742 RunInStatus => bsec_virtual_sensor_t_BSEC_OUTPUT_RUN_IN_STATUS,
743 SensorHeatCompensatedTemperature => {
744 bsec_virtual_sensor_t_BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE
745 }
746 SensorHeatCompensatedHumidity => {
747 bsec_virtual_sensor_t_BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY
748 }
749 GasPercentage => bsec_virtual_sensor_t_BSEC_OUTPUT_GAS_PERCENTAGE,
750 }
751 }
752}
753
754impl TryFrom<bsec_virtual_sensor_t> for OutputKind {
755 type Error = ConversionError;
756 fn try_from(virtual_sensor: bsec_virtual_sensor_t) -> Result<Self, ConversionError> {
757 #![allow(non_upper_case_globals)]
758 use libalgobsec_sys::*;
759 use OutputKind::*;
760 match virtual_sensor {
761 bsec_virtual_sensor_t_BSEC_OUTPUT_IAQ => Ok(Iaq),
762 bsec_virtual_sensor_t_BSEC_OUTPUT_STATIC_IAQ => Ok(StaticIaq),
763 bsec_virtual_sensor_t_BSEC_OUTPUT_CO2_EQUIVALENT => Ok(Co2Equivalent),
764 bsec_virtual_sensor_t_BSEC_OUTPUT_BREATH_VOC_EQUIVALENT => Ok(BreathVocEquivalent),
765 bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_TEMPERATURE => Ok(RawTemperature),
766 bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_PRESSURE => Ok(RawPressure),
767 bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_HUMIDITY => Ok(RawHumidity),
768 bsec_virtual_sensor_t_BSEC_OUTPUT_RAW_GAS => Ok(RawGas),
769 bsec_virtual_sensor_t_BSEC_OUTPUT_STABILIZATION_STATUS => Ok(StabilizationStatus),
770 bsec_virtual_sensor_t_BSEC_OUTPUT_RUN_IN_STATUS => Ok(RunInStatus),
771 bsec_virtual_sensor_t_BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE => {
772 Ok(SensorHeatCompensatedTemperature)
773 }
774 bsec_virtual_sensor_t_BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY => {
775 Ok(SensorHeatCompensatedHumidity)
776 }
777 bsec_virtual_sensor_t_BSEC_OUTPUT_GAS_PERCENTAGE => Ok(GasPercentage),
778 _ => Err(ConversionError::InvalidVirtualSensorId(virtual_sensor)),
779 }
780 }
781}
782
783impl TryFrom<u8> for OutputKind {
784 type Error = ConversionError;
785 fn try_from(virtual_sensor: u8) -> Result<Self, ConversionError> {
786 Self::try_from(virtual_sensor as bsec_virtual_sensor_t)
787 }
788}
789
790trait IntoResult {
791 fn into_result(self) -> Result<(), BsecError>;
792}
793
794impl IntoResult for bsec_library_return_t {
795 fn into_result(self) -> Result<(), BsecError> {
796 #![allow(non_upper_case_globals)]
797 match self {
798 libalgobsec_sys::bsec_library_return_t_BSEC_OK => Ok(()),
799 error_code => Err(BsecError::from(error_code)),
800 }
801 }
802}
803
804#[cfg(test)]
805pub mod tests {
806 use super::*;
807 use crate::bme::test_support::FakeBmeSensor;
808 use crate::clock::test_support::FakeClock;
809 use serial_test::serial;
810 use std::collections::HashMap;
811
812 #[test]
813 #[serial]
814 fn cannot_create_mulitple_bsec_at_the_same_time() {
815 let clock = FakeClock::default();
816 let first: Bsec<_, FakeClock, _> = Bsec::init(FakeBmeSensor::default(), &clock).unwrap();
817 assert!(Bsec::<_, FakeClock, _>::init(FakeBmeSensor::default(), &clock).is_err());
818 drop(first);
819 let _another = Bsec::<_, FakeClock, _>::init(FakeBmeSensor::default(), &clock).unwrap();
820 }
821
822 #[test]
823 #[serial]
824 fn basic_bsec_operation_smoke_test() {
825 let clock = FakeClock::default();
826 let sensor = FakeBmeSensor::new(Ok(vec![
827 Input {
828 sensor: InputKind::Temperature,
829 signal: 22.,
830 },
831 Input {
832 sensor: InputKind::Humidity,
833 signal: 40.,
834 },
835 Input {
836 sensor: InputKind::Pressure,
837 signal: 1000.,
838 },
839 Input {
840 sensor: InputKind::GasResistor,
841 signal: 6000.,
842 },
843 ]));
844 let mut bsec: Bsec<_, FakeClock, _> = Bsec::init(sensor, &clock).unwrap();
845 bsec.update_subscription(&[
846 SubscriptionRequest {
847 sample_rate: SampleRate::Lp,
848 sensor: OutputKind::RawTemperature,
849 },
850 SubscriptionRequest {
851 sample_rate: SampleRate::Lp,
852 sensor: OutputKind::RawHumidity,
853 },
854 SubscriptionRequest {
855 sample_rate: SampleRate::Lp,
856 sensor: OutputKind::RawPressure,
857 },
858 SubscriptionRequest {
859 sample_rate: SampleRate::Lp,
860 sensor: OutputKind::RawGas,
861 },
862 ])
863 .unwrap();
864
865 clock.advance_by(bsec.start_next_measurement().unwrap());
866 let outputs = bsec.process_last_measurement().unwrap();
867 assert!(bsec.next_measurement() >= 3_000_000_000);
868
869 let signals: HashMap<OutputKind, &Output> = outputs.iter().map(|s| (s.sensor, s)).collect();
870 assert!(
871 (signals.get(&OutputKind::RawTemperature).unwrap().signal - 22.).abs() < f64::EPSILON
872 );
873 assert!((signals.get(&OutputKind::RawHumidity).unwrap().signal - 40.).abs() < f64::EPSILON);
874 assert!(
875 (signals.get(&OutputKind::RawPressure).unwrap().signal - 1000.).abs() < f64::EPSILON
876 );
877 assert!((signals.get(&OutputKind::RawGas).unwrap().signal - 6000.).abs() < f64::EPSILON);
878 }
879
880 #[test]
881 #[serial]
882 fn roundtrip_state_smoke_test() {
883 let clock = FakeClock::default();
884 let sensor = FakeBmeSensor::default();
885 let mut bsec: Bsec<_, FakeClock, _> = Bsec::init(sensor, &clock).unwrap();
886 let state = bsec.get_state().unwrap();
887 bsec.set_state(&state).unwrap();
888 }
889
890 #[test]
891 #[serial]
892 fn configuration_roundtrip_smoke_test() {
893 let clock = FakeClock::default();
894 let sensor = FakeBmeSensor::default();
895 let mut bsec: Bsec<_, FakeClock, _> = Bsec::init(sensor, &clock).unwrap();
896 let config = bsec.get_configuration().unwrap();
897 bsec.set_configuration(&config).unwrap();
898 }
899
900 #[test]
901 fn get_version_smoke_test() {
902 let version = get_version().unwrap();
903 assert!(version.0 == 1);
904 assert!(version.1 >= 4);
905 assert!(version.1 > 4 || version.2 >= 8);
906 }
907
908 #[test]
909 #[serial]
910 fn reset_output_smoke_test() {
911 let clock = FakeClock::default();
912 let sensor = FakeBmeSensor::default();
913 let mut bsec: Bsec<_, FakeClock, _> = Bsec::init(sensor, &clock).unwrap();
914 bsec.reset_output(OutputKind::Iaq).unwrap();
915 }
916}