packages feed

obd-0.1.0.0: src/System/Hardware/ELM327/Car/MAP.hs

{-# LANGUAGE NoImplicitPrelude #-}
-- | A car that uses the intake manifold absolute pressure (MAP) to
-- calculate fuel consumption.
module System.Hardware.ELM327.Car.MAP (
  MAPProperties
, defaultProperties
, mapCar
) where

import Control.Monad.IO.Class (MonadIO)

import Numeric.Units.Dimensional.Prelude

import System.Hardware.ELM327.Connection (Con)
import System.Hardware.ELM327.Car (Car(..), I, infoT, runInfoT, defaultCar)


-- | Some fixed values needed to implement a MAP car.
data MAPProperties = MAPProperties { volumetricEfficiency :: Dimensionless Double
                                   , engineDisplacement :: Volume Double
                                   , airFuelRatio :: Dimensionless Double
                                   , fuelMassDensity :: MassDensity Double }

-- | The default MAP properties
defaultProperties :: MAPProperties
defaultProperties = MAPProperties { volumetricEfficiency = 0.75 *~ one                 -- consumer engine
                                  , engineDisplacement = 1198 *~ cubic (centi meter)   -- 1.2l
                                  , airFuelRatio = 14.7 *~ one                         -- chemically ideal
                                  , fuelMassDensity = 750 *~ (kilo gram / cubic meter) -- EUROSUPER 95
                                  }

-- | Create a car that uses the intake manifold absolute pressure (MAP)
-- to calculate fuel consumption.
mapCar :: MonadIO m => Con -> MAPProperties ->Car m
mapCar con p = let c = defaultCar con in
               c { engineFuelRate = engineFuelRate' p c
                 , massAirFlowRate = massAirFlowRate' p c}

-- | Calculate MAF from RPM, MAP and IAT
--
-- See https://web.archive.org/web/20160323073219/http://www.lightner.net/obd2guru/IMAP_AFcalc.html
massAirFlowRate' :: Monad m  => MAPProperties -> Car m -> I m (MassFlow Double)
massAirFlowRate' p c = runInfoT $ do
    rpm  <- infoT $ engineRPM c
    map' <- infoT $ intakeManifoldAbsolutePressure c
    iat  <- infoT $ intakeAirTemperature c
    let imap = rpm * map' / iat / (2 *~ one)
    let voleff = volumetricEfficiency p
    let displ = engineDisplacement p
    let mm = 28.97 *~ (gram / mole)
    let r = 8.314 *~ (joule / kelvin / mole)
    return $ imap * voleff * displ * mm / r

-- | Calculate the engine fuel rate from MAF
engineFuelRate' :: Monad m => MAPProperties -> Car m -> I m (VolumeFlow Double)
engineFuelRate' p c = runInfoT $ do
    maf <- infoT $ massAirFlowRate' p c
    return $ maf / airFuelRatio p / fuelMassDensity p