{-# LANGUAGE RankNTypes, NoMonomorphismRestriction #-} {-# OPTIONS -W #-} module Main where import System.IO import Text.ParserCombinators.Parsec import System.Environment (getArgs, getProgName) import System.Exit (exitFailure) data ControlMeas = CM { time1 :: Double, vel :: Double, steer :: Double } data Sensor = S { time2 :: Double, lat :: Double, long :: Double, orient :: Double } data Merged = M {cm :: ControlMeas, sens :: Sensor} type Table1 = [ ControlMeas ] type Table2 = [ Sensor ] type MergedT = [ Merged ] -- prev will be Nothing on startup data Tracking a = Tr {prev :: Maybe a, curr :: a, rest :: [a]} -- interpolate and merge 2 files main :: IO () main = do args <- getArgs case args of [fileName1, fileName2] -> do handle1 <- openFile fileName1 ReadMode handle2 <- openFile fileName2 ReadMode contents1 <- hGetContents handle1 contents2 <- hGetContents handle2 let list1 = tail $ convertS (parseCSV contents1) let list2 = tail $ convertS (parseCSV contents2) let tableM = interpolation (constructTab1 list1) (constructTab2 list2) writeFile "output.csv" (unlines (convertTable tableM)) putStrLn "Interpolated data written to output.csv" _ -> do progName <- getProgName hPutStrLn stderr ("Usage: " ++ progName ++ " ") hPutStrLn stderr ("Example: " ++ progName ++ " \"slam_control.csv\" \"slam_gps.csv\"") exitFailure -- parsec (CSV) csvFile = endBy line eol line = sepBy cell (char ',') cell = many (noneOf ",\n") eol = char '\n' parseCSV :: String -> Either ParseError [[String]] parseCSV input = parse csvFile "(unknown)" input -- convert table to output format (CSV) convertTable :: MergedT -> [String] convertTable = map convertCSV convertCSV :: Merged -> String convertCSV m = show (time1 control) ++ "," ++ show (vel control) ++ "," ++ show (steer control) ++ "," ++ show (lat sensors) ++ "," ++ show (long sensors) ++ "," ++ show (orient sensors) where control = cm m sensors = sens m -- Perform Interpolation interpolation :: Table1 -> Table2 -> MergedT interpolation [] [] = [] interpolation [] _ = error "not enough data to interpolate" interpolation _ [] = error "not enough data to interpolate" interpolation (x1:xs) (y1:ys) = go (Tr Nothing x1 xs) (Tr Nothing y1 ys) -- interpolation using current and previous tracking go :: Tracking ControlMeas -> Tracking Sensor -> MergedT go (Tr _ _ []) (Tr _ _ _) = [] go (Tr pr1 cur1 rst1) (Tr pr2 cur2 rst2) = let t1 = time1 cur1 t2 = time2 cur2 in case compare t1 t2 of LT -> M cur1 res : go (Tr (Just cur1) (head rst1) (tail rst1)) (Tr pr2 cur2 rst2) where S t2p latp longp orientp = maybe (S t1 0 0 0) id pr2 S t2c latc longc orientc = cur2 interp x y = ((x-y) / (t2c-t2p)) * (t1-t2p) + y res = S t1 (interp latc latp) (interp longc longp) (interp orientc orientp) EQ -> M cur1 cur2 : go (Tr (Just cur1) (head rst1) (tail rst1)) (Tr (Just cur2) (head rst2) (tail rst2)) GT -> M res cur2 : if null rst2 then [] else go (Tr pr1 cur1 rst1) (Tr (Just cur2) (head rst2) (tail rst2)) where CM t1p velp steerp = maybe (CM t2 0 0) id pr1 CM t1c velc steerc = cur1 interp x y = ((x-y) / (t1c-t1p)) * (t2-t1p) + y res = CM t2 (interp velc velp) (interp steerc steerp) -- helper functions readD :: String -> Double readD x = read x :: Double convertS :: Either ParseError a -> a convertS (Left _) = error "something went wrong in the parsing -- FIXME" convertS (Right s) = s constructTab1 :: [[String]] -> Table1 constructTab1 = map read3 where read3 :: [String] -> ControlMeas read3 (x : y : z : []) = CM (readD x) (readD y) (readD z) read3 _ = error "Table 1 should have exactly 3 entries per row" constructTab2 :: [[String]] -> Table2 constructTab2 = map read4 where read4 :: [String] -> Sensor read4 (x : y : z : w : []) = S (readD x) (readD y) (readD z) (readD w) read4 _ = error "Table 2 should have exactly 4 entries per row"