Example #1
0
def init_config_env(env_name, default_names=['config.cnf']):
    """
    Creates a new ConfigParser instance and reads the file given
    by an Environmental Variable. If variable does not exist
    a default value will be used.
    :param str env_name: Name of Environmental Variable
    :param list of str default_names: Default Name (default: 'config.cnf')
    :return ConfigParser:
    """
    if env_name in environ:
        return init_config(environ[env_name])
    else:
        c = None
        for default_name in default_names:
            try:
                c = init_config(default_name)
                return c
            except ConfigFileNotFoundError:
                log('Could not find {} ...'.format(default_name))

        if not c:
            log('Failed to load Configuration File!')
            raise ConfigFileNotFoundError('Failed to load Configuration File!')
        else:
            # This will probably never happen...
            return c
Example #2
0
    def _set_accuracy(self, val_x, val_y, mode):
        try:
            f_val_x = float(val_x)
            f_val_y = float(val_y)
            i_val_m = int(float(mode))

            if f_val_x < 0 or f_val_y < 0:
                # No Data
                self._gpsStatusIcon.image = IMAGES[IMAGE_GPSSAT_OFF]
                self._accuracyLabel.settext('---')
            else:
                if i_val_m <= 1 or i_val_m > 3:
                    # No Fix / Invalid Data
                    self._gpsStatusIcon.image = IMAGES[IMAGE_GPSSAT_ERROR]
                    self._accuracyLabel.settext('FIX')
                else:
                    fix_accuracy = (f_val_x + f_val_y) / 2
                    if i_val_m == 2:
                        # 2D FIX
                        self._gpsStatusIcon.image = IMAGES[IMAGE_GPSSAT_WARN]
                    elif i_val_m == 3:
                        # 3D Fix
                        self._gpsStatusIcon.image = IMAGES[IMAGE_GPSSAT_OK]
                    self._accuracyLabel.settext('{:>3.0f}'.format(min(fix_accuracy, 999)))

        except (ValueError, TypeError):
            log('Failed to parse EPX, EPY or MODE: {} / {} / {}'.format(val_x, val_y, mode))
            self._gpsStatusIcon.image = IMAGES[IMAGE_GPSSAT_OFF]
            self._accuracyLabel.settext('ERR')
Example #3
0
 def get_image(self, image_path):
     if image_path not in self.image_store:
         log("Image \"{}\" not loaded! Load image before use!".format(
             image_path))
         return None
     else:
         return self.image_store[image_path]
Example #4
0
 def stop_safe(self, timeout=5):
     try:
         self.stop(timeout)
         return True
     except ThreadStopTimeoutError:
         log("Stopping {} timed out after {} seconds!".format(
             self.__class__.__name__, timeout))
         return False
Example #5
0
 def _execute_command(self, command, params=None):
     if command in self._command_implementation:
         fun = self._command_implementation[command]
         if params:
             fun(params)
         else:
             fun()
     else:
         log("No function found for Redis Command {}!".format(command))
Example #6
0
 def __init__(self, type, val=None):
     """
     :param str type: OBD PID
     :param str val: (optional) value received to parse
     """
     log("Unknown or unimplemented OBD PID {} (Value was: {})".format(
         type, val))
     self.type = type
     self.val = val
Example #7
0
def delete_alive_key(r):
    """
    :param Redis r:
    """
    try:
        log("Deleting Alive Key ...")
        r.delete(MpdDataRedisKeys.KEY_ALIVE)
    finally:
        pass
Example #8
0
 def _set_longitude(self, val):
     try:
         f_val = float(val)
         if isnan(f_val) or (f_val == -360):
             self._longitudeLabel.settext('---.--------- -')
         else:
             self._longitudeLabel.settext('{:>13.9f} {}'.format(abs(f_val), 'W' if val < 0 else 'E'))
     except (ValueError, TypeError):
         log('Longitude given could not be converted to float: {}'.format(val))
         self._longitudeLabel.settext('---.--------- -')
Example #9
0
    def shutdown(self):
        try:
            if self.active_window:
                try:
                    self.active_window.destroy()
                finally:
                    pass

            self._fetcher.stop_safe()
            self._predis_fetcher.stop_safe()
            log("Shutting down CarPiUIApp ...")
            self.destroy()
        finally:
            return
Example #10
0
def init_config(filepath):
    """
    Creates a new ConfigParser instance and reads the given file
    :param str filepath: Configuration File's Path
    :return ConfigParser:
    """
    log('Reading Config File {} ...'.format(filepath))
    config = ConfigParser()
    try:
        config.readfp(open(filepath))
    except IOError:
        raise ConfigFileNotFoundError
    init_logging_from_config(config)
    return config
Example #11
0
    def _set_speed(self, val, imperial=False):
        try:
            f_val = float(val)
            if isnan(f_val) or (val == -1):
                self._speedLabel.settext('  0')
                self._speedGraph.add_data_point(0)
            else:
                self._speedLabel.settext('{:>3.0f}'.format(f_val))
                self._speedGraph.add_data_point(f_val)
        except (ValueError, TypeError):
            log('Speed given could not be converted to float: {}'.format(val))
            self._speedLabel.settext('  0')
            self._speedGraph.add_data_point(0)

        # self._speedGraph.add_data_point(datetime.now().second)
        self._speedUnitLabel.settext(' mph' if imperial else 'km/h')
Example #12
0
def parse_value(type, val):
    """
    Parses a given OBD value of a given type (PID)
    and returns the parsed value.
    If the PID is unknown / not implemented a PIDParserUnknownError
    will be raised including the type which was unknown
    :param type:
    :param val:
    :return:
    """
    if type in PARSER_MAP:
        prep_val = prepare_value(val)
        out = PARSER_MAP[type](prep_val)
        log('For {} entered {}, got {} out'.format(type, prep_val, out))
        return out
    else:
        raise ObdPidParserUnknownError(type, val)
Example #13
0
def init_pygame(config):
    """
    :param ConfigParser config:
    :return:
    """
    log('Initializing PyGame ...')
    init_passed, init_failed = pygame.init()
    log('PyGame initialized using Ver. {}, {} modules loaded, {} modules failed'
        .format(pygame.ver, init_passed, init_failed))

    mouse_visible = False
    if config.has_option(UI_CONFIG_SECTION, UI_CONFIG_KEY_SHOW_MOUSE):
        mouse_visible = config.getboolean(UI_CONFIG_SECTION,
                                          UI_CONFIG_KEY_SHOW_MOUSE)

    pygame.mouse.set_visible(mouse_visible)
    pygame.display.set_mode(
        (config.getint(UI_CONFIG_SECTION, UI_CONFIG_KEY_RES_WIDTH),
         config.getint(UI_CONFIG_SECTION, UI_CONFIG_KEY_RES_HEIGHT)))
Example #14
0
def init_io(config):
    """
    :param ConfigParser config:
    :return:
    """
    log("Configuring PyGame IO ...")

    if ENV_OUTPUT not in environ:
        if config.has_option(IO_CONFIG_SECTION, IO_CONFIG_KEY_OUTPUT_DEVICE):
            environ[ENV_OUTPUT] = config.get(IO_CONFIG_SECTION,
                                             IO_CONFIG_KEY_OUTPUT_DEVICE)

    if ENV_MOUSE_DEVICE not in environ:
        if config.has_option(IO_CONFIG_SECTION, IO_CONFIG_KEY_MOUSE_DEVICE):
            environ[ENV_MOUSE_DEVICE] = config.get(IO_CONFIG_SECTION,
                                                   IO_CONFIG_KEY_MOUSE_DEVICE)

    if ENV_MOUSE_DRIVER not in environ:
        if config.has_option(IO_CONFIG_SECTION, IO_CONFIG_KEY_MOUSE_DRIVER):
            environ[ENV_MOUSE_DRIVER] = config.get(IO_CONFIG_SECTION,
                                                   IO_CONFIG_KEY_MOUSE_DRIVER)
Example #15
0
def prepare_value(v):
    """
    :param str v:
    :return str:
    """
    log('Preparing value {}'.format(v))
    a = v.split('|')
    if len(a) >= 2 and a[1] != '>':
        log('Returning {} for {}'.format(a[1], v))
        return a[1]
    else:
        log('Returning NONE for {}'.format(v))
        return None
Example #16
0
def load_image(path, convert_alpha=True):
    image = None
    try:
        log('Loading Image {} ...'.format(path))
        with open(path) as f:
            image = pygame.image.load(f)  # type: pygame.Surface

        if image and convert_alpha:
            image = image.convert_alpha(image)
    except IOError:
        log('Could not load image {}!'.format(path))
    except pygame.error as err:
        log('PyGame did a bad while loading "{}": {}'.format(
            path, err.message))

    return image
Example #17
0
 def _do(self):
     try:
         self._fetch_data()
         self._retries = RedisBackgroundFetcher.RETRIES
     except (exceptions.ConnectionError, exceptions.TimeoutError):
         if self._retries == 0:
             log("Failed to reconnect to Redis after {} retries!".format(
                 RedisBackgroundFetcher.RETRIES))
             raise
         else:
             log("Connection to Redis lost, skipping and trying again in {} seconds ({} more times) ..."
                 .format(RedisBackgroundFetcher.RETRY_INTERVAL,
                         self._retries))
             self._retries -= 1
             sleep(RedisBackgroundFetcher.RETRY_INTERVAL)
     except SystemExit:
         log("SystemExit has been requested, stopping Fetcher Thread ...")
         self._running = False
Example #18
0
def get_redis(config):
    """
    Returns the default Redis connection
    :param ConfigParser config:
    :return Redis:
    """
    global RCONFIG_VALUE_EXPIRE
    try:
        RCONFIG_VALUE_EXPIRE = config.getint(RCONFIG_SECTION,
                                             RCONFIG_KEY_EXPIRE)
        log("The Redis values will expire after {} seconds.".format(
            RCONFIG_VALUE_EXPIRE))
    except NoOptionError:
        log("The Redis values will not expire.")
        RCONFIG_VALUE_EXPIRE = None
    except ValueError:
        log("The provided default Expire value is invalid! No expiration will be set."
            )
        RCONFIG_VALUE_EXPIRE = None

    return _get_redis(config, RCONFIG_SECTION)
Example #19
0
def init_config_env(env_name, default_names=['config.cnf']):
    """
    Creates a new ConfigParser instance and reads the file given
    by an Environmental Variable. If variable does not exist
    a default value will be used.
    :param str env_name: Name of Environmental Variable
    :param list of str default_names: Default Name (default: 'config.cnf')
    :return ConfigParser:
    """
    if env_name in environ:
        return init_config(environ[env_name])
    else:
        c = None
        for default_name in default_names:
            try:
                c = init_config(default_name)
                return c
            except ConfigFileNotFoundError:
                log('Could not find {} ...'.format(default_name))

        if not c:
            log('Failed to load Configuration File!')
            raise ConfigFileNotFoundError('Failed to load Configuration File!')
        else:
            # This will probably never happen...
            return c


if __name__ == "__main__":
    log("This script is not intended to be run standalone!")
Example #20
0
        pass


if __name__ == "__main__":
    EXIT_CODE = EXIT_CODES['OK']

    CONFIG = init_config_env('CARPI_NETD_CONF',
                             ['mpd-daemon.conf', '/etc/carpi/mpd-daemon.conf'])
    boot_print(APP_NAME)

    CONFIG_DATAPOLLER_INTERVAL = CONFIG.getfloat('DataPoller',
                                                 'interval') / 1000
    CONFIG_CONTROLLER_INTERVAL = CONFIG.getfloat('Controller',
                                                 'interval') / 1000

    log("Connecting to MPD ...")
    MPD_DATA_THREAD = MpdDataPoller(CONFIG, CONFIG_DATAPOLLER_INTERVAL)
    MPD_DATA_THREAD.start()

    log("Initializing Redis Connection ...")
    R = get_redis(CONFIG)

    log("Initializing Control Thread ...")
    MPD_CONTROL = MpdControlThread(CONFIG, R, CONFIG_CONTROLLER_INTERVAL)
    MPD_CONTROL.start()

    try:
        log("MPD Data & Control Daemon is running ...")
        while True:
            if not MPD_DATA_THREAD.isAlive() or not MPD_CONTROL.isAlive():
                raise ConnectionError()
Example #21
0
from sys import exit

APP_NAME = path.basename(__file__)

SYNC_SETTINGS = [PersistentGpsRedisKeys.KEY_TRIP_A_RECORDING]

EXIT_CODE = EXIT_CODES['OK']

if __name__ == "__main__":
    APP = None  # type: CarPiUIApp
    try:
        CONFIG = init_config_env('CARPI_UI_CONFIG',
                                 ['ui.conf', '/etc/carpi/ui.conf'])
        boot_print(APP_NAME)

        log("Initializing Data Source ...")
        R = get_redis(CONFIG)
        RP = get_persistent_redis(CONFIG)

        log("Synchronizing values ...")
        for key in SYNC_SETTINGS:
            load_synced_value(R, RP, key)

        log("Configuring UI ...")
        init_io(CONFIG)
        init_pygame(CONFIG)

        APP = CarPiUIApp(
            rect=(CONFIG.getint(UI_CONFIG_SECTION, UI_CONFIG_KEY_RES_WIDTH),
                  CONFIG.getint(UI_CONFIG_SECTION, UI_CONFIG_KEY_RES_HEIGHT) -
                  21),
Example #22
0
#            })

if __name__ == "__main__":
    EXIT_CODE = EXIT_CODES['OK']

    CONFIG = init_config_env('CARPI_GPSD_CONF',
                             ['gps-daemon.conf', '/etc/carpi/gps-daemon.conf'])
    boot_print(APP_NAME)

    CONFIG_DATAPOLLER_INTERVAL = CONFIG.getfloat('DataPoller',
                                                 'interval') / 1000
    CONFIG_RECORD_ODO = CONFIG.getboolean('ODO_Recording', 'enabled')
    CONFIG_LOCATION_POLLER_INTERVAL = CONFIG.getfloat(
        'DataPoller', 'location_polling') / 1000

    log("Initializing GPS ...")
    GPS_POLLER = GpsPoller()
    GPS_POLLER.start()

    log("Initializing Redis Connection ...")
    R = get_redis(CONFIG)
    RP = get_persistent_redis(CONFIG)

    # Disabled due to incompatibility with RPI
    # if CONFIG_LOCATION_POLLER_INTERVAL > 0:
    #     log("Initializing GPS Location Poller ...")
    #     GPS_LOC = GpsLocationPoller(GPS_POLLER, R, CONFIG_LOCATION_POLLER_INTERVAL)
    #     GPS_LOC.start()
    # else:
    #     log("GPS Location Poller is disabled. To enable, set [DataPoller].location_polling in config file > 0")
Example #23
0
 def stop(self, timeout=5):
     log("Stopping {} ...".format(self.__class__.__name__))
     self._running = False
     self.join(timeout)
     if self.isAlive():
         raise ThreadStopTimeoutError
Example #24
0
 def load_image(self, image_path):
     if image_path not in self.image_store:
         self.image_store[image_path] = load_image(image_path)
     else:
         log("Image \"{}\" already loaded".format(image_path))
Example #25
0
 def run(self):
     log("{} has started".format(self.__class__.__name__))
     while self._running:
         self._do()
         if self._interval:
             sleep(self._interval)
Example #26
0
    # print(data)
    return data


if __name__ == "__main__":
    EXIT_CODE = EXIT_CODES['OK']

    CONFIG = init_config_env('CARPI_NETD_CONF', ['net-daemon.conf', '/etc/carpi/net-daemon.conf'])
    boot_print(APP_NAME)

    CONFIG_DATAPOLLER_INTERVAL = CONFIG.getfloat('DataPoller', 'interval') / 1000
    CONFIG_IFACE_ALIAS_ETH0 = CONFIG.get('Interface_Alias', 'eth0')
    CONFIG_IFACE_ALIAS_WLAN0 = CONFIG.get('Interface_Alias', 'wlan0')
    CONFIG_IFACE_ALIAS_WLAN1 = CONFIG.get('Interface_Alias', 'wlan1')

    log("Starting Data Poller ...")
    POLLER = NetDataPoller(CONFIG_DATAPOLLER_INTERVAL)
    POLLER.start()

    log("Initialize Redis Connection ...")
    R = get_redis(CONFIG)

    try:
        log("Network Info Daemon is running ...")
        while True:
            ips = POLLER.get_current_data()
            if ips:
                r_data = prepare_dict(NetworkInfoRedisKeys.KEYS)

                if CONFIG_IFACE_ALIAS_ETH0:
                    ip = get_iface_address(ips, CONFIG_IFACE_ALIAS_ETH0)
Example #27
0
    def __init__(self,
                 rect,
                 redis,
                 pers_redis,
                 title='CarPi',
                 fullscreen=False):
        log("Initializing CarPiUIApp ...")
        pqApp.__init__(self, rect=rect, title=title, fullscreen=fullscreen)

        # Internal Data Storage & Processing
        self.image_store = {}
        self._pages = {}
        self._redis_pages = {}
        self._predis_pages = {}
        self._current_page = None  # type: str
        self._fetcher = None  # type: RedisBackgroundFetcher
        self._predis_fetcher = None  # type: RedisBackgroundFetcher
        self._redis = redis  # type: Redis
        self._pers_redis = pers_redis  # type: Redis

        # Tabs
        self._gps_tab_button = None  # type: Button
        self._music_tab_button = None  # type: Button
        self._settings_tab_button = None  # type: Button

        # Status Icons
        self._gps_status_icon = None  # type: Image
        self._obd_status_icon = None  # type: Image

        # GPS Data
        self._speed_label = None  # type: Text
        self._speed_unit = None  # type: Text
        self._speed_graph = None  # type: Graph
        self._location_label = None  # type: Text

        self._gps_status_icon = None  # type: Image
        self._car_status_icon = None  # type: Image

        # Trip Data
        self._trip_meter = None  # type: Text
        self._odo_meter = None  # type: Text
        self._trip_odo_unit = None  # type: Text

        # Status Bar
        self._ethernet_status_icon = None  # type: Image
        self._wlan0_status_icon = None  # type: Image
        self._time_label = None  # type: Text

        # Music Display
        self._current_title = None  # type: Text
        self._current_artist = None  # type: Text
        self._current_album = None  # type: Text

        # Music Status
        self._current_song_time = None  # type: Text
        self._current_song_time_bar = None  # type: ProgressBar

        # Music Controls
        self._next_song_button = None  # type: Button
        self._prev_song_button = None  # type: Button
        self._play_song_button = None  # type: Button

        # Load Resources
        self._load()

        # Init Controls
        self._init_controls()

        # Define UI Pages
        gps_page = [
            self._speed_label, self._speed_graph, self._speed_unit,
            self._odo_meter, self._trip_meter, self._trip_odo_unit,
            self._location_label
        ]
        music_page = [
            self._current_title, self._current_artist, self._current_album,
            self._current_song_time, self._current_song_time_bar,
            self._next_song_button, self._prev_song_button,
            self._play_song_button
        ]
        settings_page = []

        self._pages[CarPiUIApp.PAGE_GPS] = gps_page
        self._pages[CarPiUIApp.PAGE_MUSIC] = music_page
        self._pages[CarPiUIApp.PAGE_SETTINGS] = settings_page

        # Define Redis Pages
        gps_r_page = [
            # Alive Keys
            GpsRedisKeys.KEY_ALIVE,
            NetworkInfoRedisKeys.KEY_ALIVE,
            MpdDataRedisKeys.KEY_ALIVE,

            # Always present keys
            NetworkInfoRedisKeys.KEY_ETH0_IP,
            NetworkInfoRedisKeys.KEY_WLAN0_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN0_SSID,
            NetworkInfoRedisKeys.KEY_WLAN1_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN1_SSID,
            GpsRedisKeys.KEY_SPEED,
            GpsRedisKeys.KEY_SPEED_KMH,
            MpdDataRedisKeys.KEY_STATE,

            # OBD & Fuel Consumption
            ObdRedisKeys.KEY_ALIVE,
            ObdRedisKeys.KEY_ENGINE_RPM,
            ObdRedisKeys.KEY_INTAKE_TEMP,
            ObdRedisKeys.KEY_INTAKE_MAP,
            ObdRedisKeys.KEY_VEHICLE_SPEED,

            # Specific Keys
            GpsRedisKeys.KEY_EPX,
            GpsRedisKeys.KEY_EPY
        ]
        music_r_page = [
            # Alive Keys
            GpsRedisKeys.KEY_ALIVE,
            NetworkInfoRedisKeys.KEY_ALIVE,
            MpdDataRedisKeys.KEY_ALIVE,

            # Always present keys
            NetworkInfoRedisKeys.KEY_ETH0_IP,
            NetworkInfoRedisKeys.KEY_WLAN0_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN0_SSID,
            NetworkInfoRedisKeys.KEY_WLAN1_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN1_SSID,
            GpsRedisKeys.KEY_SPEED,
            GpsRedisKeys.KEY_SPEED_KMH,
            MpdDataRedisKeys.KEY_STATE,

            # OBD & Fuel Consumption
            ObdRedisKeys.KEY_ALIVE,
            ObdRedisKeys.KEY_ENGINE_RPM,
            ObdRedisKeys.KEY_INTAKE_TEMP,
            ObdRedisKeys.KEY_INTAKE_MAP,
            ObdRedisKeys.KEY_VEHICLE_SPEED,

            # Specific Keys
            MpdDataRedisKeys.KEY_SONG_TITLE,
            MpdDataRedisKeys.KEY_SONG_ARTIST,
            MpdDataRedisKeys.KEY_SONG_ALBUM,
            MpdDataRedisKeys.KEY_CURRENT_TIME,
            MpdDataRedisKeys.KEY_CURRENT_TIME_FORMATTED
        ]
        settings_r_page = [
            # Alive Keys
            GpsRedisKeys.KEY_ALIVE,
            NetworkInfoRedisKeys.KEY_ALIVE,
            MpdDataRedisKeys.KEY_ALIVE,

            # Always present keys
            NetworkInfoRedisKeys.KEY_ETH0_IP,
            NetworkInfoRedisKeys.KEY_WLAN0_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN0_SSID,
            NetworkInfoRedisKeys.KEY_WLAN1_STRENGTH,
            NetworkInfoRedisKeys.KEY_WLAN1_SSID,
            GpsRedisKeys.KEY_SPEED_KMH,
            MpdDataRedisKeys.KEY_STATE,

            # OBD & Fuel Consumption
            ObdRedisKeys.KEY_ALIVE,
            ObdRedisKeys.KEY_ENGINE_RPM,
            ObdRedisKeys.KEY_INTAKE_TEMP,
            ObdRedisKeys.KEY_INTAKE_MAP,
            ObdRedisKeys.KEY_VEHICLE_SPEED,

            # Specific Keys
        ]

        self._redis_pages[CarPiUIApp.PAGE_GPS] = gps_r_page
        self._redis_pages[CarPiUIApp.PAGE_MUSIC] = music_r_page
        self._redis_pages[CarPiUIApp.PAGE_SETTINGS] = settings_r_page

        # Define Persistent Redis Pages
        self._predis_pages[CarPiUIApp.PAGE_GPS] = PersistentGpsRedisKeys.KEYS
        self._predis_pages[CarPiUIApp.PAGE_MUSIC] = []
        self._predis_pages[CarPiUIApp.PAGE_SETTINGS] = []
Example #28
0
"""

from pqGUI import *
from PygameUtils import load_image, init_pygame
from CarPiLogging import log, boot_print, end_print, print_unhandled_exception, EXIT_CODES
from CarPiConfig import init_config_env
from RedisUtils import get_redis, RedisBackgroundFetcher
from RedisKeys import GpsRedisKeys, NetworkInfoRedisKeys
from math import isnan
from redis import exceptions as redis_exceptions
from datetime import datetime
import os
import sys
import time

log("FBDEV: " + os.environ.get("SDL_FBDEV", '<nodev>'))
log("MOUSE: [" + os.environ.get("SDL_MOUSEDRV", '<nodrv>') + "] " + os.environ.get("SDL_MOUSEDEV", '<nodev>'))

APP_NAME = os.path.basename(__file__)

EXIT_CODE = EXIT_CODES['OK']

FONT_DEFAULT = os.path.join('res', 'fonts', 'Vera.ttf')
FONT_7SEGM = os.path.join('res', 'fonts', 'DigitalCounter7.ttf')
FONT_DOTMTX = os.path.join('res', 'fonts', 'scoreboard.ttf')

IMAGE_GPSSAT_OFF = os.path.join('res', 'img', 'gpssat-off.png')
IMAGE_GPSSAT_OK = os.path.join('res', 'img', 'gpssat-ok.png')
IMAGE_GPSSAT_WARN = os.path.join('res', 'img', 'gpssat-warn.png')
IMAGE_GPSSAT_ERROR = os.path.join('res', 'img', 'gpssat-error.png')
IMAGE_CAR_OFF = os.path.join('res', 'img', 'car-off.png')