Exemple #1
0
def get_bitmap_of_weather_conditions(m: Metar.Metar, weather_desc: WeatherCondDescription):
    bitmap = [0] * weather_desc.total_conditions

    if m.present_weather() != "":
        current_conditions = m.present_weather().split("; ")
        for cond in current_conditions:
            bitmap[weather_desc.weather_cond_dict[cond]] = 1

    return bitmap
Exemple #2
0
def from_metar_to_list_of_features(airport: str, timestamp: datetime, m: Metar.Metar) -> List:

    result = [
        airport,
        timestamp,
        m.code,

        m.mod,

        # m.station_id,

        m.wind_speed.value("mps") if m.wind_speed else None,
        m.wind_gust.value("mps") if m.wind_gust else None,


        m.wind_dir.value() if m.wind_dir else None,
        m.wind_dir_from.value() if m.wind_dir_from else None,
        m.wind_dir_to.value() if m.wind_dir_to else None,

        m.vis.value("m") if m.vis else None,

        m.max_vis.value("m") if m.max_vis else None,
        m.max_vis_dir.value() if m.max_vis_dir else None,

        m.runway[0][1].value("m") if m.runway else None,
        m.runway[0][2].value("m") if m.runway else None,


        m.temp.value("C") if m.temp else None,
        m.dewpt.value("C") if m.dewpt else None,
        m.press.value("HPA") if m.press else None,

        str(m.sky_conditions("; ")),

        # str(m.weather),
        str(m.present_weather()),
        # str(m.recent),

        # str(m.sky),
        # str(m.windshear),

        # str(m._trend),
        # str(m._trend_groups),
        # str(m._remarks),
        # str(m._unparsed_groups),
        # str(m._unparsed_remarks),

    ]
    return result
Exemple #3
0
def internal_metar(metar_text: str) -> Dict[str, Any]:
    """
    Convert the output of the Metar parser into a dictionary which could be
    queried in a document database

    :param metar_text: N
    :return: a Dict with decoded METAR information
    """
    document = {}
    wxd = Metar(metar_text)
    document["station"] = wxd.station_id

    if wxd.type:
        document["type"] = wxd.type

    if wxd.time:
        document["time"] = wxd.time.isoformat()+'Z'

    if wxd.temp:
        document["temp"] = wxd.temp.value(units="F")

    if wxd.dewpt:
        document["dewpt"] = wxd.dewpt.value(units="F")

    if "temp" in document and "dewpt" in document:
        document["humidity"] = rel_humidity(document["temp"], document["dewpt"])

    if wxd.wind_speed:
        document["wind_speed"] = wxd.wind_speed.value(units="mph")

    if wxd.wind_dir:
        document["wind_dir"] = wxd.wind_dir.value()

    if wxd.vis:
        document["visibility"] = wxd.vis.value(units="sm")

    if wxd.press:
        document["pressure"] = wxd.press.value(units="mb")

    if wxd.sky:
        document["sky"] = wxd.sky_conditions()

    if wxd.press_sea_level:
        document["pressure"] = wxd.press_sea_level.value("mb")

    document["code"] = wxd.code

    return document
Exemple #4
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            if msg.find("day is out of range for month") > 0 and now.day == 1:
                now -= datetime.timedelta(days=1)
                continue
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: "+msg)
                print("    --> now: %s month: %s, year: %s" % (now, now.month,
                                                               now.year))
                sys.exit()
                return None
        except Exception, exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
Exemple #5
0
def parse_page(page):
    lines = filter(clean_line, page.split("\n"))
    records = map(get_date_and_standard_metar, lines)
    data = {
        "observation_time": [],
        "temperature": [],
        "dew_point": [],
        "pressure": [],
        "humidity": [],
    }

    for observation_time, raw_metar in records:
        try:
            m = Metar(raw_metar)
        except ParserError as err:
            logger.error(
                "Error parsing METAR: %s - %s",
                observation_time,
                raw_metar,
                exc_info=True,
            )
            continue
        temperature = m.temp.value()
        dew_point = m.dewpt.value()
        data["observation_time"].append(observation_time)
        data["temperature"].append(temperature)
        data["dew_point"].append(dew_point)
        data["pressure"].append(m.press.value())
        data["humidity"].append(humidity(temperature, dew_point))

    return pd.DataFrame.from_dict(data)
def parse_page(page):
    lines = filter(clean_line, page.split('\n'))
    records = map(get_date_and_standard_metar, lines)
    data = {
        'observation_time': [],
        'temperature': [],
        'dew_point': [],
        'pressure': [],
        'humidity': [],
    }

    for observation_time, raw_metar in records:
        try:
            m = Metar(raw_metar)
        except ParserError as err:
            logger.error(
                'Error parsing METAR: %s - %s',
                observation_time,
                raw_metar,
                exc_info=True,
            )
            continue
        temperature = m.temp.value()
        dew_point = m.dewpt.value()
        data['observation_time'].append(observation_time)
        data['temperature'].append(temperature)
        data['dew_point'].append(dew_point)
        data['pressure'].append(m.press.value())
        data['humidity'].append(humidity(temperature, dew_point))

    return pd.DataFrame.from_dict(data)
Exemple #7
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: " + msg)
                return None
        except Exception, exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
Exemple #8
0
def main():
    """Go Main Go"""
    pgconn = get_dbconn("asos")
    icursor = pgconn.cursor()
    icursor.execute("SET TIME ZONE 'UTC'")
    icursor2 = pgconn.cursor()

    sts = datetime.datetime(2011, 1, 1)
    ets = datetime.datetime(2012, 1, 1)
    interval = datetime.timedelta(days=1)
    now = sts
    while now < ets:
        icursor.execute("""
      select valid, station, metar from t%s
      where metar is not null and valid >= '%s' and valid < '%s'
        """ % (now.year, now.strftime("%Y-%m-%d"),
               (now+interval).strftime("%Y-%m-%d")))
        total = 0
        for row in icursor:
            try:
                mtr = Metar(row[2], row[0].month, row[0].year)
            except Exception as _exp:
                continue
            sql = 'update t%s SET ' % (now.year,)
            if mtr.max_temp_6hr:
                sql += "max_tmpf_6hr = %s," % (mtr.max_temp_6hr.value("F"),)
            if mtr.min_temp_6hr:
                sql += "min_tmpf_6hr = %s," % (mtr.min_temp_6hr.value("F"),)
            if mtr.max_temp_24hr:
                sql += "max_tmpf_24hr = %s," % (mtr.max_temp_24hr.value("F"),)
            if mtr.min_temp_24hr:
                sql += "min_tmpf_24hr = %s," % (mtr.min_temp_24hr.value("F"),)
            if mtr.precip_3hr:
                sql += "p03i = %s," % (mtr.precip_3hr.value("IN"),)
            if mtr.precip_6hr:
                sql += "p06i = %s," % (mtr.precip_6hr.value("IN"),)
            if mtr.precip_24hr:
                sql += "p24i = %s," % (mtr.precip_24hr.value("IN"),)
            if mtr.press_sea_level:
                sql += "mslp = %s," % (mtr.press_sea_level.value("MB"),)
            if mtr.weather:
                pwx = []
                for x in mtr.weather:
                    pwx.append(("").join([a for a in x if a is not None]))
                sql += "presentwx = '%s'," % ((",".join(pwx))[:24], )
            if sql == "update t%s SET " % (now.year,):
                continue
            sql = "%s WHERE station = '%s' and valid = '%s'" % (sql[:-1],
                                                                row[1],
                                                                row[0])
            # print sql
            icursor2.execute(sql)
            total += 1
            if total % 1000 == 0:
                print('Done total: %s now: %s' % (total, now))
                pgconn.commit()
        now += interval
    icursor2.close()
    pgconn.commit()
    pgconn.close()
Exemple #9
0
def test_set_weather_from_metar(metar, out, weather_test_file, out_file):
    in_metar = Metar(metar)
    with Miz(weather_test_file) as miz:
        _randomize_weather(miz.mission)
        miz.zip(out_file)
    emiz.weather.mizfile.set_weather_from_metar(metar, out_file, out_file)
    out_metar = Metar(
        emiz.weather.mizfile.get_metar_from_mission(out_file, 'UGTB'))
    assert in_metar.temp.value() == out_metar.temp.value()
    assert int(
        in_metar.wind_speed.value('MPS')) == out_metar.wind_speed.value('MPS')
    assert in_metar.wind_dir.value() == out_metar.wind_dir.value()
    with Miz(out_file) as miz:
        assert miz.mission.weather.wind_at_ground_level_dir == emiz.weather.utils.reverse_direction(
            in_metar.wind_dir.value())
        assert miz.mission.weather.wind_at2000_speed >= miz.mission.weather.wind_at_ground_level_speed
        assert miz.mission.weather.wind_at8000_speed >= miz.mission.weather.wind_at_ground_level_speed
        for key in out:
            if isinstance(out[key], tuple):
                assert out[key][0] <= getattr(miz.mission.weather,
                                              key) <= out[key][1]
            else:
                assert getattr(miz.mission.weather, key) == out[key], key
Exemple #10
0
def weather_today_job():
    # request data from NOAA server (METAR of Nancy-Essey Airport)
    r = requests.get(
        'http://tgftp.nws.noaa.gov/data/observations/metar/stations/LFSN.TXT',
        timeout=10.0,
        headers={'User-Agent': USER_AGENT})
    # check error
    if r.status_code == 200:
        # extract METAR message
        metar_msg = r.content.decode().split('\n')[1]
        # METAR parse
        obs = Metar(metar_msg)
        # init and populate d_today dict
        d_today = {}
        # message date and time
        if obs.time:
            d_today['update_iso'] = obs.time.strftime('%Y-%m-%dT%H:%M:%SZ')
            d_today['update_fr'] = dt_utc_to_local(
                obs.time).strftime('%H:%M %d/%m')
        # current temperature
        if obs.temp:
            d_today['temp'] = round(obs.temp.value('C'))
        # current dew point
        if obs.dewpt:
            d_today['dewpt'] = round(obs.dewpt.value('C'))
        # current pressure
        if obs.press:
            d_today['press'] = round(obs.press.value('hpa'))
        # current wind speed
        if obs.wind_speed:
            d_today['w_speed'] = round(obs.wind_speed.value('KMH'))
        # current wind gust
        if obs.wind_gust:
            d_today['w_gust'] = round(obs.wind_gust.value('KMH'))
        # current wind direction
        if obs.wind_dir:
            # replace 'W'est by 'O'uest
            d_today['w_dir'] = obs.wind_dir.compass().replace('W', 'O')
        # weather status str
        d_today['descr'] = 'n/a'
        # store to redis
        DB.main.set_as_json('json:weather:today:nancy', d_today, ex=2 * 3600)
Exemple #11
0
def add_ground_wind(airport_code, aloft_wind):
    """
	Add the wind information at the ground for the given station code.
	"""
    raw = requests.get(BASE_ADDR.format(airport_code.upper()))
    if raw.status_code == 404:
        raw = requests.get(BASE_ADDR.format('K' + airport_code.upper()))

    if raw.status_code == 404:
        raise ValueError('The given station code is invalid.')

    ground_wind = {'altitude': 0, 'direction': 0, 'speed': 0}

    for line in raw.text.split('\n'):
        if airport_code.upper() in line:
            line = line.strip()
            observation = Metar(line)
            ground_wind['direction'] = observation.wind_dir.value()
            ground_wind['speed'] = observation.wind_speed.value(units='KT')

    return [ground_wind] + aloft_wind
Exemple #12
0
    def process_data(self):
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            try:
                self.log.info("Processing Metar data...")

                with open(
                        os.path.join(os.path.dirname(__file__),
                                     "../metar/stations.json")) as in_file:
                    icao = json.load(in_file)

                now = arrow.utcnow()
                hour = now.hour
                minute = now.minute

                if minute < 45:
                    current_cycle = hour
                else:
                    current_cycle = hour + 1 % 24

                stations = {}
                for cycle in (current_cycle - 1, current_cycle):
                    file = f"http://tgftp.nws.noaa.gov/data/observations/metar/cycles/{cycle:02d}Z.TXT"
                    self.log.info(f"Processing '{file}' ...")

                    request = requests.get(file,
                                           stream=True,
                                           timeout=(self.connect_timeout,
                                                    self.read_timeout))
                    for line in request.iter_lines():
                        if line:
                            data = line.decode("iso-8859-1")
                            try:
                                # Is this line a date with format "2017/05/12 23:55" ?
                                arrow.get(data, "YYYY/MM/DD HH:mm")
                                continue
                                # Catch also ValueError because https://github.com/crsmithdev/arrow/issues/535
                            except (arrow.parser.ParserError, ValueError):
                                try:
                                    metar = Metar(data, strict=False)
                                    # wind_dir could be NONE if 'dir' is 'VRB'
                                    if metar.wind_speed:
                                        if metar.station_id not in stations:
                                            stations[metar.station_id] = {}
                                        key = arrow.get(
                                            metar.time).int_timestamp
                                        stations[metar.station_id][key] = metar
                                except Exception as e:
                                    self.log.warning(
                                        f"Error while parsing METAR data: {e}")
                                    continue

                for metar_id in stations:
                    metar = next(iter(stations[metar_id].values()))
                    try:
                        name, short_name, default_name, lat, lon, altitude, tz = (
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                        )

                        checkwx_key = f"metar/checkwx/{metar.station_id}"
                        if not self.redis.exists(checkwx_key):
                            try:
                                self.log.info("Calling api.checkwx.com...")
                                request = requests.get(
                                    f"https://api.checkwx.com/station/{metar.station_id}",
                                    headers={
                                        "Accept": "application/json",
                                        "X-API-Key": self.checkwx_api_key
                                    },
                                    timeout=(self.connect_timeout,
                                             self.read_timeout),
                                )
                                if request.status_code == 401:
                                    raise UsageLimitException(
                                        request.json()["errors"][0]["message"])
                                elif request.status_code == 429:
                                    raise UsageLimitException(
                                        "api.checkwx.com rate limit exceeded")

                                try:
                                    checkwx_data = request.json()["data"][0]
                                    if "icao" not in checkwx_data:
                                        raise ProviderException(
                                            "Invalid CheckWX data")
                                except (ValueError, KeyError):
                                    checkwx_json = request.json()
                                    messages = []
                                    if type(checkwx_json["data"]) is list:
                                        messages.extend(checkwx_json["data"])
                                    else:
                                        messages.append(checkwx_json["data"])
                                    raise ProviderException(
                                        f'CheckWX API error: {",".join(messages)}'
                                    )

                                self.add_redis_key(
                                    checkwx_key,
                                    {
                                        "data":
                                        json.dumps(checkwx_data),
                                        "date":
                                        arrow.now().format(
                                            "YYYY-MM-DD HH:mm:ssZZ"),
                                    },
                                    self.checkwx_cache_duration,
                                )
                            except TimeoutError as e:
                                raise e
                            except UsageLimitException as e:
                                self.add_redis_key(
                                    checkwx_key,
                                    {
                                        "error":
                                        repr(e),
                                        "date":
                                        arrow.now().format(
                                            "YYYY-MM-DD HH:mm:ssZZ"),
                                    },
                                    self.usage_limit_cache_duration,
                                )
                            except Exception as e:
                                if not isinstance(e, ProviderException):
                                    self.log.exception(
                                        "Error while getting CheckWX data")
                                else:
                                    self.log.warning(
                                        f"Error while getting CheckWX data: {e}"
                                    )
                                self.add_redis_key(
                                    checkwx_key,
                                    {
                                        "error":
                                        repr(e),
                                        "date":
                                        arrow.now().format(
                                            "YYYY-MM-DD HH:mm:ssZZ"),
                                    },
                                    self.checkwx_cache_duration,
                                )

                        if not self.redis.hexists(checkwx_key, "error"):
                            checkwx_data = json.loads(
                                self.redis.hget(checkwx_key, "data"))

                            station_type = checkwx_data.get("type", None)
                            if station_type:
                                name = f'{checkwx_data["name"]} {station_type}'
                            else:
                                name = checkwx_data["name"]
                            city = checkwx_data.get("city", None)
                            if city:
                                if station_type:
                                    short_name = f"{city} {station_type}"
                                else:
                                    short_name = city
                            else:
                                default_name = checkwx_data["name"]

                            lat = checkwx_data["latitude"]["decimal"]
                            lon = checkwx_data["longitude"]["decimal"]
                            try:
                                tz = checkwx_data["timezone"]["tzid"]
                            except KeyError:
                                raise ProviderException(
                                    "Unable to get the timezone")
                            elevation = checkwx_data.get("elevation", None)
                            altitude = None
                            if elevation:
                                if "meters" in elevation:
                                    altitude = Q_(elevation["meters"],
                                                  ureg.meters)
                                elif "feet" in elevation:
                                    altitude = Q_(elevation["feet"], ureg.feet)

                        if metar.station_id in icao:
                            lat = lat or icao[metar.station_id]["lat"]
                            lon = lon or icao[metar.station_id]["lon"]
                            default_name = default_name or icao[
                                metar.station_id]["name"]

                        station = self.save_station(
                            metar.station_id,
                            short_name,
                            name,
                            lat,
                            lon,
                            StationStatus.GREEN,
                            altitude=altitude,
                            tz=tz,
                            url=os.path.join(
                                self.provider_url,
                                f"site?id={metar.station_id}&db=metar"),
                            default_name=default_name
                            or f"{metar.station_id} Airport",
                            lookup_name=f"{metar.station_id} Airport ICAO",
                        )
                        station_id = station["_id"]

                        if metar.station_id not in icao:
                            self.log.warning(
                                f"Missing '{metar.station_id}' ICAO in database. Is it '{station['name']}'?"
                            )

                        measures_collection = self.measures_collection(
                            station_id)
                        new_measures = []
                        for key in stations[metar_id]:
                            metar = stations[metar_id][key]
                            if not self.has_measure(measures_collection, key):
                                temp = self.get_quantity(
                                    metar.temp, self.temperature_units)
                                dew_point = self.get_quantity(
                                    metar.dewpt, self.temperature_units)
                                humidity = self.compute_humidity(
                                    dew_point, temp)
                                measure = self.create_measure(
                                    station,
                                    key,
                                    self.get_direction(metar.wind_dir),
                                    self.get_quantity(metar.wind_speed,
                                                      self.speed_units),
                                    self.get_quantity(
                                        metar.wind_gust or metar.wind_speed,
                                        self.speed_units),
                                    temperature=temp,
                                    humidity=humidity,
                                    pressure=Pressure(
                                        qfe=None,
                                        qnh=self.get_quantity(
                                            metar.press, self.pressure_units),
                                        qff=self.get_quantity(
                                            metar.press_sea_level,
                                            self.pressure_units),
                                    ),
                                )
                                new_measures.append(measure)

                        self.insert_new_measures(measures_collection, station,
                                                 new_measures)

                    except ProviderException as e:
                        self.log.warning(
                            f"Error while processing station '{metar_id}': {e}"
                        )
                    except Exception as e:
                        self.log.exception(
                            f"Error while processing station '{metar_id}': {e}"
                        )

            except Exception as e:
                self.log.exception(f"Error while processing Metar: {e}")

        self.log.info("Done !")
Exemple #13
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            if msg.find("day is out of range for month") > 0 and now.day == 1:
                now -= datetime.timedelta(days=1)
                continue
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: " + msg)
                print("    --> now: %s month: %s, year: %s" %
                      (now, now.month, now.year))
                sys.exit()
                return None
        except Exception as exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
    if mtr is None or mtr.time is None:
        return None

    ob = OB()
    ob.metar = mstr[:254]

    gts = datetime.datetime(
        mtr.time.year,
        mtr.time.month,
        mtr.time.day,
        mtr.time.hour,
        mtr.time.minute,
    )
    gts = gts.replace(tzinfo=pytz.UTC)
    # When processing data on the last day of the month, we get GMT times
    # for the first of this month
    if gts.day == 1 and now.day > 10:
        tm = now + datetime.timedelta(days=1)
        gts = gts.replace(year=tm.year, month=tm.month, day=tm.day)

    ob.valid = gts

    if mtr.temp:
        ob.tmpf = mtr.temp.value("F")
    if mtr.dewpt:
        ob.dwpf = mtr.dewpt.value("F")

    if mtr.wind_speed:
        ob.sknt = mtr.wind_speed.value("KT")
    if mtr.wind_gust:
        ob.gust = mtr.wind_gust.value("KT")

    if mtr.wind_dir and mtr.wind_dir.value() != "VRB":
        ob.drct = mtr.wind_dir.value()

    if mtr.vis:
        ob.vsby = mtr.vis.value("SM")

    if mtr.press:
        ob.alti = mtr.press.value("IN")

    if mtr.press_sea_level:
        ob.mslp = mtr.press_sea_level.value("MB")

    if mtr.precip_1hr:
        ob.p01i = mtr.precip_1hr.value("IN")

    # Do something with sky coverage
    for i in range(len(mtr.sky)):
        (c, h, _) = mtr.sky[i]
        setattr(ob, "skyc%s" % (i + 1), c)
        if h is not None:
            setattr(ob, "skyl%s" % (i + 1), h.value("FT"))

    if mtr.max_temp_6hr:
        ob.max_tmpf_6hr = mtr.max_temp_6hr.value("F")
    if mtr.min_temp_6hr:
        ob.min_tmpf_6hr = mtr.min_temp_6hr.value("F")
    if mtr.max_temp_24hr:
        ob.max_tmpf_24hr = mtr.max_temp_24hr.value("F")
    if mtr.min_temp_24hr:
        ob.min_tmpf_6hr = mtr.min_temp_24hr.value("F")
    if mtr.precip_3hr:
        ob.p03i = mtr.precip_3hr.value("IN")
    if mtr.precip_6hr:
        ob.p06i = mtr.precip_6hr.value("IN")
    if mtr.precip_24hr:
        ob.p24i = mtr.precip_24hr.value("IN")

    # Presentwx
    if mtr.weather:
        pwx = []
        for x in mtr.weather:
            pwx.append(("").join([a for a in x if a is not None]))
        ob.wxcodes = pwx

    return ob
#code='METAR OMDW 220000Z 09006KT 2500 BR NSC 26/25 Q1004 BECMG 0800 BCFG'
#code='METAR LCEN 302350Z 28008KT CAVOK 17/09 Q1016 NOSIG'
#code='SPECI CZCP 302353Z AUTO 11016KT 2 3/4SM OVC005 OVC015 OVC020 M03/ A2989 RMK ICG PAST HR'
#code='METAR VANP 302340Z 00000KT 3500 HZ NSC 21/18 Q1014 TEMPO 3000 HZ/BR'
#code='METAR SEGU 010000Z 22009KT 9999 SCT023 BKN100 26/20 Q1009 RMK A2983 NOSIG'
#code='METAR SEQM 010000Z 12007KT CAVOK 16/03 Q1024 NOSIG RMK A3024'
#code='SPECI CZCP 302355Z AUTO 11017KT 2 3/4SM OVC004 OVC020 M03/ A2989 RMK ICG PAST HR'
#code='METAR LCEN 302350Z 28008KT CAVOK 17/09 Q1016 NOSIG'
#code='METAR VABB 310040Z 08008KT 3000 HZ NSC 25/18 Q1011 NOSIG='
#code='METAR COR VABB 310040Z 08008KT 3000 HZ NSC 25/18 Q1011 NOSIG='
code='METAR LEMD 222200Z 35007KT 310V020 CAVOK 27/10 Q1015 NOSIG='

print "-----------------------------------------------------------------------"
print "METAR: ",code
print "-----------------------------------------------------------------------"

# Initialize a Metar object with the coded report
obs = Metar(code)

print obs.string()
#print obs.string().split('\n')

metar_header = {}
metar_header['ICAO_code'] = obs.station_id
metar_header['origin_time'] = obs.time.isoformat(' ')
metar_header['origin_date'] = obs.time.day
metar_header['origin_hours'] = obs.time.isoformat()[11:13]
metar_header['origin_minutes'] = obs.time.isoformat()[14:16]
print metar_header

Exemple #15
0
    def process_data(self):
        try:
            self.log.info('Processing Metar data...')

            with open(os.path.join(os.path.dirname(__file__), 'metar/stations.json')) as in_file:
                icao = json.load(in_file)

            now = arrow.utcnow()
            hour = now.hour
            minute = now.minute

            if minute < 45:
                current_cycle = hour
            else:
                current_cycle = hour + 1 % 24

            stations = {}
            for cycle in (current_cycle-1, current_cycle):
                file = f'http://tgftp.nws.noaa.gov/data/observations/metar/cycles/{cycle:02d}Z.TXT'
                self.log.info(f"Processing '{file}' ...")

                request = requests.get(file, stream=True, timeout=(self.connect_timeout, self.read_timeout))
                for line in request.iter_lines():
                    if line:
                        data = line.decode('iso-8859-1')
                        try:
                            # Is this line a date with format "2017/05/12 23:55" ?
                            arrow.get(data, 'YYYY/MM/DD HH:mm')
                            continue
                            # Catch also ValueError because https://github.com/crsmithdev/arrow/issues/535
                        except (arrow.parser.ParserError, ValueError):
                            try:
                                metar = Metar(data, strict=False)
                                # wind_dir could be NONE if 'dir' is 'VRB'
                                if metar.wind_speed:
                                    if metar.station_id not in stations:
                                        stations[metar.station_id] = {}
                                    key = arrow.get(metar.time).timestamp
                                    stations[metar.station_id][key] = metar
                            except Exception as e:
                                self.log.warn(f'Error while parsing METAR data: {e}')
                                continue

            for metar_id in stations:
                metar = next(iter(stations[metar_id].values()))
                try:
                    name, short_name, default_name, lat, lon, altitude, tz = None, None, None, None, None, None, None

                    checkwx_key = f'metar/checkwx/{metar.station_id}'
                    if not self.redis.exists(checkwx_key):
                        try:
                            self.log.info('Calling api.checkwx.com...')
                            request = requests.get(
                                f'https://api.checkwx.com/station/{metar.station_id}',
                                headers={'Accept': 'application/json', 'X-API-Key': self.checkwx_api_key},
                                timeout=(self.connect_timeout, self.read_timeout)
                            )
                            if request.status_code == 401:
                                raise UsageLimitException(request.json()['errors'][0]['message'])
                            elif request.status_code == 429:
                                raise UsageLimitException('api.checkwx.com rate limit exceeded')

                            try:
                                checkwx_data = request.json()['data'][0]
                                if 'icao' not in checkwx_data:
                                    raise ProviderException('Invalid CheckWX data')
                            except (ValueError, KeyError):
                                checkwx_json = request.json()
                                messages = []
                                if type(checkwx_json['data']) is list:
                                    messages.extend(checkwx_json['data'])
                                else:
                                    messages.append(checkwx_json['data'])
                                raise ProviderException(f'CheckWX API error: {",".join(messages)}')

                            self.add_redis_key(checkwx_key, {
                                'data': json.dumps(checkwx_data),
                                'date': arrow.now().format('YYYY-MM-DD HH:mm:ssZZ'),
                            }, self.checkwx_cache_duration)
                        except TimeoutError as e:
                            raise e
                        except UsageLimitException as e:
                            self.add_redis_key(checkwx_key, {
                                'error': repr(e),
                                'date': arrow.now().format('YYYY-MM-DD HH:mm:ssZZ'),
                            }, self.usage_limit_cache_duration)
                        except Exception as e:
                            if not isinstance(e, ProviderException):
                                self.log.exception('Error while getting CheckWX data')
                            else:
                                self.log.warn(f'Error while getting CheckWX data: {e}')
                            self.add_redis_key(checkwx_key, {
                                'error': repr(e),
                                'date': arrow.now().format('YYYY-MM-DD HH:mm:ssZZ'),
                            }, self.checkwx_cache_duration)

                    if not self.redis.hexists(checkwx_key, 'error'):
                        checkwx_data = json.loads(self.redis.hget(checkwx_key, 'data'))

                        station_type = checkwx_data.get('type', None)
                        if station_type:
                            name = f'{checkwx_data["name"]} {station_type}'
                        else:
                            name = checkwx_data['name']
                        city = checkwx_data.get('city', None)
                        if city:
                            if station_type:
                                short_name = f'{city} {station_type}'
                            else:
                                short_name = city
                        else:
                            default_name = checkwx_data['name']

                        lat = checkwx_data['latitude']['decimal']
                        lon = checkwx_data['longitude']['decimal']
                        tz = checkwx_data['timezone']['tzid']
                        elevation = checkwx_data.get('elevation', None)
                        altitude = None
                        if elevation:
                            if 'meters' in elevation:
                                altitude = Q_(elevation['meters'], ureg.meters)
                            elif 'feet' in elevation:
                                altitude = Q_(elevation['feet'], ureg.feet)

                    if metar.station_id in icao:
                        lat = lat or icao[metar.station_id]['lat']
                        lon = lon or icao[metar.station_id]['lon']
                        default_name = default_name or icao[metar.station_id]['name']

                    station = self.save_station(
                        metar.station_id,
                        short_name,
                        name,
                        lat,
                        lon,
                        Status.GREEN,
                        altitude=altitude,
                        tz=tz,
                        url=os.path.join(self.provider_url, f'site?id={metar.station_id}&db=metar'),
                        default_name=default_name or f'{metar.station_id} Airport',
                        lookup_name=f'{metar.station_id} Airport ICAO')
                    station_id = station['_id']

                    if metar.station_id not in icao:
                        self.log.warn(f"Missing '{metar.station_id}' ICAO in database. Is it '{station['name']}'?")

                    measures_collection = self.measures_collection(station_id)
                    new_measures = []
                    for key in stations[metar_id]:
                        metar = stations[metar_id][key]
                        if not self.has_measure(measures_collection, key):
                            temp = self.get_quantity(metar.temp, self.temperature_units)
                            dew_point = self.get_quantity(metar.dewpt, self.temperature_units)
                            humidity = self.compute_humidity(dew_point, temp)
                            measure = self.create_measure(
                                station,
                                key,
                                self.get_direction(metar.wind_dir),
                                self.get_quantity(metar.wind_speed, self.speed_units),
                                self.get_quantity(metar.wind_gust or metar.wind_speed, self.speed_units),
                                temperature=temp,
                                humidity=humidity,
                                pressure=Pressure(qfe=None,
                                                  qnh=self.get_quantity(metar.press, self.pressure_units),
                                                  qff=self.get_quantity(metar.press_sea_level, self.pressure_units))
                            )
                            new_measures.append(measure)

                    self.insert_new_measures(measures_collection, station, new_measures)

                except ProviderException as e:
                    self.log.warn(f"Error while processing station '{metar_id}': {e}")
                except Exception as e:
                    self.log.exception(f"Error while processing station '{metar_id}': {e}")

        except Exception as e:
            self.log.exception(f'Error while processing Metar: {e}')

        self.log.info('Done !')
#code='METAR COR VABB 310040Z 08008KT 3000 HZ NSC 25/18 Q1011 NOSIG='
#code='METAR LEMD 222200Z 35007KT 310V020 CAVOK 27/10 Q1015 NOSIG='
#code='METAR UMMM 151230Z 08004MPS 9999 FEW025 OVC100 07/00 Q1041 R12/CLRD60 NOSIG RMK QFE760'
#code='METAR UMMS 151230Z 01002MPS 010V070 CAVOK 09/00 Q1042 R13/CLRD// NOSIG'
# code='METAR EYKA 151250Z 10007KT 060V130 CAVOK 09/00 Q1042 R08/090095'
#code='METAR UMMS 151230Z 01002MPS 010V070 CAVOK 09/00 Q1042 R88CLRD95 NOSIG'
#code='METAR UMMS 151230Z 01002MPS 010V070 CAVOK 09/00 Q1042 R24CLRD93 NOSIG'
#code='METAR UMMS 151230Z 01002MPS 010V070 CAVOK 09/00 Q1042 R99/421594 NOSIG'
#code='METAR UMMS 151230Z 01002MPS 010V070 CAVOK 09/00 Q1042 R99/SNOCLO NOSIG'
#code='METAR LTAJ 151220Z 24012KT 9999 SCT040 BKN100 14/04 Q1017 TEMPO Q1019 TEMPO 27015G25KT -TSRA'
code='SPECI KBTV 151208Z 36007KT 1SM R15/P6000FT -SN BR BKN006 OVC019 M01/M03 A2965'

print "-----------------------------------------------------------------------"
print "METAR: ",code
print "-----------------------------------------------------------------------"

# Initialize a Metar object with the coded report
obs = Metar(code)

print obs.string()
print obs.json()
#print obs.string().split('\n')

metar_header = {}
metar_header['ICAO_code'] = obs.station_id
metar_header['origin_time'] = obs.time.isoformat(' ')
metar_header['origin_date'] = obs.time.day
metar_header['origin_hours'] = obs.time.isoformat()[11:13]
metar_header['origin_minutes'] = obs.time.isoformat()[14:16]
print metar_header
Exemple #17
0
 def __init__(self, text, **kwargs):
     """Wrapper"""
     Metar.__init__(self, text, **kwargs)
     self.iemid = None
     self.network = None
Exemple #18
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: "+msg)
                return None
        except Exception as exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
    if mtr is None or mtr.time is None:
        return None

    ob = OB()
    ob.metar = mstr[:254]
    ob.valid = now

    if mtr.temp:
        ob.tmpf = mtr.temp.value("F")
    if mtr.dewpt:
        ob.dwpf = mtr.dewpt.value("F")

    if mtr.wind_speed:
        ob.sknt = mtr.wind_speed.value("KT")
    if mtr.wind_gust:
        ob.gust = mtr.wind_gust.value("KT")

    if mtr.wind_dir and mtr.wind_dir.value() != "VRB":
        ob.drct = mtr.wind_dir.value()

    if mtr.vis:
        ob.vsby = mtr.vis.value("SM")

    # see pull request #38
    if mtr.press and mtr.press != mtr.press_sea_level:
        ob.alti = mtr.press.value("IN")

    if mtr.press_sea_level:
        ob.mslp = mtr.press_sea_level.value("MB")

    if mtr.precip_1hr:
        ob.p01i = mtr.precip_1hr.value("IN")

    # Do something with sky coverage
    for i in range(len(mtr.sky)):
        (c, h, _) = mtr.sky[i]
        setattr(ob, 'skyc%s' % (i+1), c)
        if h is not None:
            setattr(ob, 'skyl%s' % (i+1), h.value("FT"))

    if mtr.max_temp_6hr:
        ob.max_tmpf_6hr = mtr.max_temp_6hr.value("F")
    if mtr.min_temp_6hr:
        ob.min_tmpf_6hr = mtr.min_temp_6hr.value("F")
    if mtr.max_temp_24hr:
        ob.max_tmpf_24hr = mtr.max_temp_24hr.value("F")
    if mtr.min_temp_24hr:
        ob.min_tmpf_6hr = mtr.min_temp_24hr.value("F")
    if mtr.precip_3hr:
        ob.p03i = mtr.precip_3hr.value("IN")
    if mtr.precip_6hr:
        ob.p06i = mtr.precip_6hr.value("IN")
    if mtr.precip_24hr:
        ob.p24i = mtr.precip_24hr.value("IN")

    # Presentwx
    if mtr.weather:
        pwx = []
        for x in mtr.weather:
            pwx.append(("").join([a for a in x if a is not None]))
        ob.presentwx = (",".join(pwx))[:24]

    return ob
#code = 'METAR SVVA 092200Z 36004KT 9999 -DZ BKN016'
#code = 'LEMD 111230Z 35006KT 250V030 CAVOK 28/03 Q1019 NOSIG'
#code='METAR SVVA 092200Z 36004KT 9999 -DZ BKN016 30/21 Q1011'
#code = 'METAR CBBC 120000Z AUTO 32005KT 9SM OVC060 19/05 A3032 RMK SLP269'
#code = 'KMMU 112345Z 25007KT 10SM SKC 24/21 A2986'
code = 'METAR CYLK 120000Z 25008KT 15SM BKN080 08/01 A2995 RMK AC5 9.0/3.5/0/NIL/LAST OBS/NXT 121400Z SLP152'




print "-----------------------------------------------------------------------"
print "METAR: ",code
print "-----------------------------------------------------------------------"

# Initialize a Metar object with the coded report
obs = Metar(code)

# Print the individual data

# The 'station_id' attribute is a string.
print "station: %s" % obs.station_id

if obs.type:
  print "type: %s" % obs.report_type()

# The 'time' attribute is a datetime object
if obs.time:
  print "time: %s" % obs.time.ctime()

# The 'temp' and 'dewpt' attributes are temperature objects
if obs.temp:
Exemple #20
0
    def loopRun(self):

        # Get sim data.
        self.getPyuipcData()

        # Get best suitable Airport.
        self.getAirport()

        # Handle if no airport found.
        if self.airport is None:
            self.logger.info(
                'No airport found, sleeping for {} seconds...'.format(
                    self.SLEEP_TIME))
            return self.SLEEP_TIME
        else:
            self.logger.info('Airport: {}.'.format(self.airport))

        # Get whazzup file
        if not self.debug:
            self.getWhazzupText()
        else:
            self.getWhazzupTextDebug()

        # Read whazzup text and get a station.
        self.parseWhazzupText()

        # Check if station online.
        if self.atisRaw is not None:
            self.logger.info('Station found, decoding Atis.')
        else:
            # Actions, if no station online.
            self.logger.info('No station online, using metar only.')
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                self.metar = Metar(self.getAirportMetar(), strict=False)

            self.parseVoiceMetar()

            # Parse atis voice with metar only.
            self.atisVoice = '{}, {}.'.format(
                self.airportInfos[self.airport][3], self.metarVoice)

            # Read the metar.
            self.readVoice()

            return self.SLEEP_TIME

        # Parse ATIS.
        # Information.
        self.getInfoIdentifier()
        self.parseVoiceInformation()

        # Metar.
        if not self.ivac2:
            self.parseMetar(self.atisRaw[2].strip())
        else:
            for ar in self.atisRaw:
                if ar.startswith('METAR'):
                    self.parseMetar(ar.replace('METAR ', '').strip())
                    break

        self.parseVoiceMetar()

        # Runways / TRL / TA
        self.parseRawRwy()
        self.parseVoiceRwy()

        # comment.
        self.parseVoiceComment()

        # Compose complete atis voice string.
        self.atisVoice = '{} {} {} {} Information {}, out.'.format(
            self.informationVoice, self.rwyVoice, self.commentVoice,
            self.metarVoice, self.informationIdentifier)

        # Read the string.
        self.readVoice()

        # After successful reading.
        return 0
Exemple #21
0
 def __init__(self, text, **kwargs):
     """Wrapper"""
     Metar.__init__(self, text, **kwargs)
     self.iemid = None
     self.network = None
Exemple #22
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: " + msg)
                return None
        except Exception as exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
    if mtr is None or mtr.time is None:
        return None

    ob = OB()
    ob.metar = mstr[:254]
    ob.valid = now

    if mtr.temp:
        ob.tmpf = mtr.temp.value("F")
    if mtr.dewpt:
        ob.dwpf = mtr.dewpt.value("F")

    if mtr.wind_speed:
        ob.sknt = mtr.wind_speed.value("KT")
    if mtr.wind_gust:
        ob.gust = mtr.wind_gust.value("KT")

    # Calc some stuff
    if ob.tmpf is not None and ob.dwpf is not None:
        ob.relh = relative_humidity_from_dewpoint(
            ob.tmpf * units('degF'),
            ob.dwpf * units('degF')).to(units('percent')).magnitude
        if ob.sknt is not None:
            ob.feel = apparent_temperature(ob.tmpf * units('degF'),
                                           ob.relh * units('percent'),
                                           ob.sknt * units('knots')).to(
                                               units('degF')).magnitude

    if mtr.wind_dir and mtr.wind_dir.value() != "VRB":
        ob.drct = mtr.wind_dir.value()

    if mtr.vis:
        ob.vsby = mtr.vis.value("SM")

    # see pull request #38
    if mtr.press and mtr.press != mtr.press_sea_level:
        ob.alti = mtr.press.value("IN")

    if mtr.press_sea_level:
        ob.mslp = mtr.press_sea_level.value("MB")

    if mtr.precip_1hr:
        ob.p01i = mtr.precip_1hr.value("IN")

    # Do something with sky coverage
    for i in range(len(mtr.sky)):
        (c, h, _) = mtr.sky[i]
        setattr(ob, 'skyc%s' % (i + 1), c)
        if h is not None:
            setattr(ob, 'skyl%s' % (i + 1), h.value("FT"))

    if mtr.max_temp_6hr:
        ob.max_tmpf_6hr = mtr.max_temp_6hr.value("F")
    if mtr.min_temp_6hr:
        ob.min_tmpf_6hr = mtr.min_temp_6hr.value("F")
    if mtr.max_temp_24hr:
        ob.max_tmpf_24hr = mtr.max_temp_24hr.value("F")
    if mtr.min_temp_24hr:
        ob.min_tmpf_6hr = mtr.min_temp_24hr.value("F")
    if mtr.precip_3hr:
        ob.p03i = mtr.precip_3hr.value("IN")
    if mtr.precip_6hr:
        ob.p06i = mtr.precip_6hr.value("IN")
    if mtr.precip_24hr:
        ob.p24i = mtr.precip_24hr.value("IN")

    # Presentwx
    if mtr.weather:
        pwx = []
        for wx in mtr.weather:
            val = "".join([a for a in wx if a is not None])
            if val == "" or val == len(val) * "/":
                continue
            pwx.append(val)
        ob.wxcodes = pwx

    return ob
Exemple #23
0
 def parseMetar(self, metarString):
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         self.metar = Metar(metarString, strict=False)
Exemple #24
0
class VoiceAtis(object):

    STATION_SUFFIXES = ['TWR', 'APP', 'GND', 'DEL', 'DEP']

    SPEECH_RATE = 150

    SLEEP_TIME = 3  # s

    RADIO_RANGE = 180  # nm

    OFFSETS = [
        (0x034E, 'H'),  # com1freq
        (0x3118, 'H'),  # com2freq
        (0x3122, 'b'),  # radioActive
        (0x0560, 'l'),  # ac Latitude
        (0x0568, 'l'),  # ac Longitude
    ]

    WHAZZUP_URL = 'http://api.ivao.aero/getdata/whazzup/whazzup.txt.gz'
    WHAZZUP_METAR_URL = 'http://wx.ivao.aero/metar.php'

    OUR_AIRPORTS_URL = 'http://ourairports.com/data/'

    COM1_FREQUENCY_DEBUG = 199.99

    # EDDS
    #     COM2_FREQUENCY_DEBUG = 126.12
    #     LAT_DEBUG = 48.687
    #     LON_DEBUG = 9.205

    # EDDM
    #     COM2_FREQUENCY_DEBUG = 123.12
    #     LAT_DEBUG = 48.353
    #     LON_DEBUG = 11.786

    # LIRF
    COM2_FREQUENCY_DEBUG = 121.85
    LAT_DEBUG = 41.8
    LON_DEBUG = 12.2

    # LIBR
    COM2_FREQUENCY_DEBUG = 121.85
    LAT_DEBUG = 41.8
    LON_DEBUG = 12.2

    WHAZZUP_TEXT_DEBUG = r'H:\My Documents\Sonstiges\voiceAtis\whazzup_1.txt'

    ## Setup the VoiceAtis object.
    # Inits logger.
    # Downloads airport data.
    def __init__(self, **optional):
        #TODO: Remove the debug code when tested properly.
        #TODO: Improve logged messages.
        #TODO: Create GUI.

        # Process optional arguments.
        self.debug = optional.get('Debug', debug)

        # Get file path.
        self.rootDir = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))

        # Init logging.
        self.logger = VaLogger(os.path.join(self.rootDir, 'voiceAtis', 'logs'))

        # First log message.
        self.logger.info('voiceAtis started')

        # Read file with airport frequencies and coordinates.
        self.logger.info('Downloading airport data. This may take some time.')
        self.getAirportData()
        self.logger.info('Finished downloading airport data.')

        # Show debug Info
        #TODO: Remove for release.
        if self.debug:
            self.logger.info('Debug mode on.')
            self.logger.setLevel(ConsoleLevel='debug')

    ## Establishs pyuipc connection.
    # Return 'True' on success or if pyuipc not installed.
    # Return 'False' on fail.
    def connectPyuipc(self):
        try:
            self.pyuipcConnection = pyuipc.open(0)
            self.pyuipcOffsets = pyuipc.prepare_data(self.OFFSETS)
            self.logger.info('FSUIPC connection established.')
            return True
        except NameError:
            self.pyuipcConnection = None
            self.logger.warning(
                'Error using PYUIPC, running voiceAtis without it.')
            return True
        except:
            self.logger.warning(
                'FSUIPC: No simulator detected. Start you simulator first!')
            return False

    ## Runs an infinite loop.
    # i.E. for use without GUI.
    def runLoop(self):

        # Establish pyuipc connection
        result = False
        while not result:
            result = self.connectPyuipc()
            if not result:
                self.logger.info('Retrying in 20 seconds.')
                time.sleep(20)

        # Infinite loop.
        try:
            while True:
                timeSleep = self.loopRun()
                time.sleep(timeSleep)

        except KeyboardInterrupt:
            # Actions at Keyboard Interrupt.
            self.logger.info('Loop interrupted by user.')
            if pyuipcImported:
                self.pyuipc.close()

    ## One cyle of a loop.
    # Returns the requested sleep time.
    def loopRun(self):

        # Get sim data.
        self.getPyuipcData()

        # Get best suitable Airport.
        self.getAirport()

        # Handle if no airport found.
        if self.airport is None:
            self.logger.info(
                'No airport found, sleeping for {} seconds...'.format(
                    self.SLEEP_TIME))
            return self.SLEEP_TIME
        else:
            self.logger.info('Airport: {}.'.format(self.airport))

        # Get whazzup file
        if not self.debug:
            self.getWhazzupText()
        else:
            self.getWhazzupTextDebug()

        # Read whazzup text and get a station.
        self.parseWhazzupText()

        # Check if station online.
        if self.atisRaw is not None:
            self.logger.info('Station found, decoding Atis.')
        else:
            # Actions, if no station online.
            self.logger.info('No station online, using metar only.')
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                self.metar = Metar(self.getAirportMetar(), strict=False)

            self.parseVoiceMetar()

            # Parse atis voice with metar only.
            self.atisVoice = '{}, {}.'.format(
                self.airportInfos[self.airport][3], self.metarVoice)

            # Read the metar.
            self.readVoice()

            return self.SLEEP_TIME

        # Parse ATIS.
        # Information.
        self.getInfoIdentifier()
        self.parseVoiceInformation()

        # Metar.
        if not self.ivac2:
            self.parseMetar(self.atisRaw[2].strip())
        else:
            for ar in self.atisRaw:
                if ar.startswith('METAR'):
                    self.parseMetar(ar.replace('METAR ', '').strip())
                    break

        self.parseVoiceMetar()

        # Runways / TRL / TA
        self.parseRawRwy()
        self.parseVoiceRwy()

        # comment.
        self.parseVoiceComment()

        # Compose complete atis voice string.
        self.atisVoice = '{} {} {} {} Information {}, out.'.format(
            self.informationVoice, self.rwyVoice, self.commentVoice,
            self.metarVoice, self.informationIdentifier)

        # Read the string.
        self.readVoice()

        # After successful reading.
        return 0

    ## Downloads and reads the whazzup from IVAO
    def getWhazzupText(self):
        urllib.urlretrieve(self.WHAZZUP_URL, 'whazzup.txt.gz')
        with gzip.open('whazzup.txt.gz', 'rb') as f:
            self.whazzupText = f.read().decode('iso-8859-15')
        os.remove('whazzup.txt.gz')

    ## Reads a whazzup file on disk.
    # For debug purposes.
    def getWhazzupTextDebug(self):
        with open(self.WHAZZUP_TEXT_DEBUG) as whazzupFile:
            self.whazzupText = whazzupFile.read()
        pass

    ## Find a station of the airport and read the ATIS string.
    def parseWhazzupText(self):
        # Find an open station
        for st in self.STATION_SUFFIXES:
            matchObj = re.search('{}\w*?_{}'.format(self.airport, st),
                                 self.whazzupText)

            if matchObj is not None:
                break

        if matchObj is not None:
            # Extract ATIS.
            lineStart = matchObj.start()
            lineEnd = self.whazzupText.find('\n', matchObj.start())
            stationInfo = self.whazzupText[lineStart:lineEnd].split(':')
            self.ivac2 = bool(int(stationInfo[39][0]) - 1)
            self.atisTextRaw = stationInfo[35].encode('iso-8859-15')
            self.atisRaw = stationInfo[35].encode('iso-8859-15').split('^§')
        else:
            self.atisRaw = None

    def parseMetar(self, metarString):
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            self.metar = Metar(metarString, strict=False)

    ## Parse runway and transition data.
    # Get active runways for arrival and departure.
    # Get transistion level and altitude.
    def parseRawRwy(self):
        self.rwyInformation = [None, None, None, None]
        if not self.ivac2:
            strSplit = self.atisRaw[3].split(' / ')

            for sp in strSplit:
                # ARR.
                if sp[0:3] == 'ARR':
                    self.rwyInformation[0] = []
                    arr = sp.replace('ARR RWY ', '').strip()
                    starts = []
                    for ma in re.finditer('\d{2}[RLC]?', arr):
                        starts.append(ma.start())
                    for st in range(len(starts)):
                        if st < len(starts) - 1:
                            rwy = arr[starts[st]:starts[st + 1]]
                        else:
                            rwy = arr[starts[st]:]
                        curRwy = [rwy[0:2], None, None, None]
                        if 'L' in rwy:
                            curRwy[1] = 'Left'
                        if 'C' in rwy:
                            curRwy[2] = 'Center'
                        if 'R' in rwy:
                            curRwy[3] = 'Right'
                        self.rwyInformation[0].append(curRwy)

                # DEP.
                elif sp[0:3] == 'DEP':
                    self.rwyInformation[1] = []
                    dep = sp.replace('DEP RWY ', '').strip()
                    starts = []
                    for ma in re.finditer('\d{2}[RLC]?', dep):
                        starts.append(ma.start())
                    for st in range(len(starts)):
                        if st < len(starts) - 1:
                            rwy = dep[starts[st]:starts[st + 1]]
                        else:
                            rwy = dep[starts[st]:]
                        curRwy = [rwy[0:2], None, None, None]
                        if 'L' in rwy:
                            curRwy[1] = 'Left'
                        if 'C' in rwy:
                            curRwy[2] = 'Center'
                        if 'R' in rwy:
                            curRwy[3] = 'Right'
                        self.rwyInformation[1].append(curRwy)

                # TRL/TA
                elif sp[0:3] == 'TRL':
                    self.rwyInformation[2] = sp.strip().replace('TRL FL', '')

                elif sp[0:2] == 'TA':
                    self.rwyInformation[3] = sp.strip().replace('TA ',
                                                                '').replace(
                                                                    'FT', '')
        # Ivac 2
        else:
            for ar in self.atisRaw:
                if ar.startswith('TA'):
                    trlTaSplit = ar.split(' / ')
                    self.rwyInformation[3] = trlTaSplit[0].replace('TA ', '')
                    self.rwyInformation[2] = trlTaSplit[1].replace('TRL', '')

                elif ar.startswith('ARR'):
                    curRwy = [ar[8:10], None, None, None]
                    if 'L' in ar[8:]:
                        curRwy[1] = 'Left'
                    if 'C' in ar[8:]:
                        curRwy[2] = 'Center'
                    if 'R' in ar[8:]:
                        curRwy[3] = 'Right'
                    if self.rwyInformation[0] is None:
                        self.rwyInformation[0] = [curRwy]
                    else:
                        self.rwyInformation[0].append(curRwy)

                elif ar.startswith('DEP'):
                    curRwy = [ar[8:10], None, None, None]
                    if 'L' in ar[8:]:
                        curRwy[1] = 'Left'
                    if 'C' in ar[8:]:
                        curRwy[2] = 'Center'
                    if 'R' in ar[8:]:
                        curRwy[3] = 'Right'
                    if self.rwyInformation[1] is None:
                        self.rwyInformation[1] = [curRwy]
                    else:
                        self.rwyInformation[1].append(curRwy)

    ## Generate a string of the metar for voice generation.
    def parseVoiceMetar(self):
        self.metarVoice = 'Met report'

        # Time
        hours = parseVoiceInt('{:02d}'.format(self.metar._hour))
        minutes = parseVoiceInt('{:02d}'.format(self.metar._min))
        self.metarVoice = '{} time {} {}'.format(self.metarVoice, hours,
                                                 minutes)

        # Wind
        if self.metar.wind_speed._value != 0:
            if self.metar.wind_dir is not None:
                self.metarVoice = '{}, wind {}, {}'.format(
                    self.metarVoice,
                    parseVoiceString(self.metar.wind_dir.string()),
                    parseVoiceString(self.metar.wind_speed.string()))
            else:
                self.metarVoice = '{}, wind variable, {}'.format(
                    self.metarVoice,
                    parseVoiceString(self.metar.wind_speed.string()))
        else:
            self.metarVoice = '{}, wind calm'.format(
                self.metarVoice, self.metar.wind_dir.string(),
                self.metar.wind_speed.string())

        if self.metar.wind_gust is not None:
            self.metarVoice = '{}, maximum {}'.format(
                self.metarVoice,
                parseVoiceString(self.metar.wind_gust.string()))

        if self.metar.wind_dir_from is not None:
            self.metarVoice = '{}, variable between {} and {}'.format(
                self.metarVoice,
                parseVoiceString(self.metar.wind_dir_from.string()),
                parseVoiceString(self.metar.wind_dir_to.string()))

        # Visibility.
        #TODO: implement directions
        self.metarVoice = '{}, visibility {}'.format(self.metarVoice,
                                                     self.metar.vis.string())

        # runway visual range
        rvr = self.metar.runway_visual_range().replace(';', ',')
        if rvr:
            rvrNew = ''
            lastEnd = 0
            rvrPattern = re.compile('[0123]\d[LCR]?(?=,)')
            for ma in rvrPattern.finditer(rvr):
                rwyRaw = rvr[ma.start():ma.end()]
                rwyStr = parseVoiceInt(rwyRaw[0:2])
                if len(rwyRaw) > 2:
                    if rwyRaw[2] == 'L':
                        rwyStr = '{} left'.format(rwyStr)
                    elif rwyRaw[2] == 'C':
                        rwyStr = '{} center'.format(rwyStr)
                    elif rwyRaw[2] == 'R':
                        rwyStr = '{} right'.format(rwyStr)
                rvrNew = '{}{}{}'.format(rvrNew, rvr[lastEnd:ma.start()],
                                         rwyStr)
                lastEnd = ma.end()

            rvrNew = '{}{}'.format(rvrNew, rvr[lastEnd:])

            self.metarVoice = '{}, visual range {}'.format(
                self.metarVoice, rvrNew)

        # weather phenomena
        if self.metar.weather:
            self.metarVoice = '{}, {}'.format(
                self.metarVoice,
                self.metar.present_weather().replace(';', ','))

        # clouds
        if self.metar.sky:
            self.metarVoice = '{}, {}'.format(
                self.metarVoice,
                self.metar.sky_conditions(',').replace(',', ', ').replace(
                    'a few', 'few'))
        elif 'CAVOK' in self.metar.code:
            self.metarVoice = '{}, clouds and visibility ok'.format(
                self.metarVoice)

        # runway condition
        #TODO: Implement runway conditions
        # Not implemented in python-metar

        # temperature
        tempValue = parseVoiceInt(str(int(self.metar.temp._value)))
        if self.metar.temp._units == 'C':
            tempUnit = 'degree Celsius'
        else:
            tempUnit = 'degree Fahrenheit'

        self.metarVoice = '{}, temperature {} {}'.format(
            self.metarVoice, tempValue, tempUnit)

        # dew point
        dewptValue = parseVoiceInt(str(int(self.metar.dewpt._value)))
        if self.metar.dewpt._units == 'C':
            dewptUnit = 'degree Celsius'
        else:
            dewptUnit = 'degree Fahrenheit'

        self.metarVoice = '{}, dew point {} {}'.format(self.metarVoice,
                                                       dewptValue, dewptUnit)

        # QNH
        if self.metar.press._units == 'MB':
            pressValue = parseVoiceInt(str(int(self.metar.press._value)))
            self.metarVoice = '{}, Q N H {} hectopascal'.format(
                self.metarVoice, pressValue)
        else:
            self.metarVoice = '{}, Altimeter {}'.format(
                self.metarVoice, parseVoiceString(self.metar.press.string()))

        #TODO: implement trend

        self.metarVoice = '{},'.format(self.metarVoice)

    ## Generate a string of the information identifier for voice generation.
    def parseVoiceInformation(self):
        if not self.ivac2:
            timeMatch = re.search(r'\d{4}z', self.atisRaw[1])
            startInd = timeMatch.start()
            endInd = timeMatch.end() - 1
            timeStr = parseVoiceInt(self.atisRaw[1][startInd:endInd])

            self.informationVoice = '{} {}.'.format(
                self.atisRaw[1][0:startInd - 1], timeStr)

        else:
            information = self.atisRaw[1].split(' ')
            airport = information[0]
            airport = self.airportInfos[airport][3]
            time = parseVoiceInt(information[4][0:4])

            self.informationVoice = '{} Information {} recorded at {}.'.format(
                airport, self.informationIdentifier, time)

    ## Generate a string of the runway information for voice generation.
    def parseVoiceRwy(self):
        self.rwyVoice = ''

        # ARR.
        if self.rwyInformation[0] is not None:
            self.rwyVoice = '{}Arrival runway '.format(self.rwyVoice)
            for arr in self.rwyInformation[0]:
                if arr[1:4].count(None) == 3:
                    self.rwyVoice = '{}{} and '.format(self.rwyVoice,
                                                       parseVoiceInt(arr[0]))
                else:
                    for si in arr[1:4]:
                        if si is not None:
                            self.rwyVoice = '{}{} {} and '.format(
                                self.rwyVoice, parseVoiceInt(arr[0]), si)
            self.rwyVoice = '{},'.format(self.rwyVoice[0:-5])

        # DEP.
        if self.rwyInformation[1] is not None:
            self.rwyVoice = '{} Departure runway '.format(self.rwyVoice)
            for dep in self.rwyInformation[1]:
                if dep[1:4].count(None) == 3:
                    self.rwyVoice = '{}{} and '.format(self.rwyVoice,
                                                       parseVoiceInt(dep[0]))
                else:
                    for si in dep[1:4]:
                        if si is not None:
                            self.rwyVoice = '{}{} {} and '.format(
                                self.rwyVoice, parseVoiceInt(dep[0]), si)
            self.rwyVoice = '{}, '.format(self.rwyVoice[0:-5])

        # TRL
        if self.rwyInformation[2] is not None:
            self.rwyVoice = '{}Transition level {}, '.format(
                self.rwyVoice, parseVoiceInt(self.rwyInformation[2]))

        # TA
        if self.rwyInformation[3] is not None:
            self.rwyVoice = '{}Transition altitude {} feet,'.format(
                self.rwyVoice, self.rwyInformation[3])

    ## Generate a string of ATIS comment for voice generation.
    def parseVoiceComment(self):
        if not self.ivac2:
            self.commentVoice = '{},'.format(parseVoiceString(self.atisRaw[4]))
        else:
            self.commentVoice = ''

    ## Reads the atis string using voice generation.
    def readVoice(self):
        # Init currently Reading with None.
        self.currentlyReading = None

        self.logger.debug('Voice Text is: {}'.format(self.atisVoice))

        if pyttsxImported:
            # Set properties currently reading
            self.currentlyReading = self.airport

            # Init voice engine.
            self.engine = pyttsx.init()

            # Set properties.
            voices = self.engine.getProperty('voices')
            for vo in voices:
                if 'english' in vo.name.lower():
                    self.engine.setProperty('voice', vo.id)
                    self.logger.debug('Using voice: {}'.format(vo.name))
                    break

            self.engine.setProperty('rate', self.SPEECH_RATE)

            # Start listener and loop.
            self.engine.connect('started-word', self.onWord)

            # Say complete ATIS
            self.engine.say(self.atisVoice)
            self.logger.info('Start reading.')
            self.engine.runAndWait()
            self.logger.info('Reading finished.')
            self.engine = None

        else:
            self.logger.warning(
                'Speech engine not initalized, no reading. Sleeping for {} seconds...'
                .format(self.SLEEP_TIME))
            time.sleep(self.SLEEP_TIME)

    ## Callback for stop of reading.
    # Stops reading if frequency change/com deactivation/out of range.
    def onWord(self, name, location, length):  # @UnusedVariable
        self.getPyuipcData()
        self.getAirport()

        if self.airport != self.currentlyReading:
            self.engine.stop()
            self.currentlyReading = None

    ## Reads current frequency and COM status.
    def getPyuipcData(self):

        if pyuipcImported:
            results = pyuipc.read(self.pyuipcOffsets)

            # frequency
            hexCode = hex(results[0])[2:]
            self.com1frequency = float('1{}.{}'.format(hexCode[0:2],
                                                       hexCode[2:]))
            hexCode = hex(results[1])[2:]
            self.com2frequency = float('1{}.{}'.format(hexCode[0:2],
                                                       hexCode[2:]))

            # radio active
            #TODO: Test accuracy of this data (with various planes and sims)
            radioActiveBits = list(map(int, '{0:08b}'.format(results[2])))
            if radioActiveBits[2]:
                self.com1active = True
                self.com2active = True
            elif radioActiveBits[0]:
                self.com1active = True
                self.com2active = False
            elif radioActiveBits[1]:
                self.com1active = False
                self.com2active = True
            else:
                self.com1active = False
                self.com2active = False

            # lat lon
            self.lat = results[3] * (90.0 / (10001750.0 * 65536.0 * 65536.0))
            self.lon = results[4] * (360.0 /
                                     (65536.0 * 65536.0 * 65536.0 * 65536.0))

        else:
            self.com1frequency = self.COM1_FREQUENCY_DEBUG
            self.com2frequency = self.COM2_FREQUENCY_DEBUG
            self.com1active = True
            self.com2active = True
            self.lat = self.LAT_DEBUG
            self.lon = self.LON_DEBUG

        # Logging.
        if self.com1active:
            com1activeStr = 'active'
        else:
            com1activeStr = 'inactive'
        if self.com2active:
            com2activeStr = 'active'
        else:
            com2activeStr = 'inactive'

        self.logger.debug('COM 1: {} ({}), COM 2: {} ({})'.format(
            self.com1frequency, com1activeStr, self.com2frequency,
            com2activeStr))


#         self.logger.debug('COM 1 active: {}, COM 2 active: {}'.format(self.com1active,self.com2active))

## Determine if there is an airport aplicable for ATIS reading.

    def getAirport(self):
        self.airport = None
        frequencies = []
        if self.com1active:
            frequencies.append(self.com1frequency)
        if self.com2active:
            frequencies.append(self.com2frequency)

        if frequencies:
            distanceMin = self.RADIO_RANGE + 1
            for ap in self.airportInfos:
                distance = gcDistanceNm(self.lat, self.lon,
                                        self.airportInfos[ap][1],
                                        self.airportInfos[ap][2])
                if (
                        floor(self.airportInfos[ap][0] * 100) / 100
                ) in frequencies and distance < self.RADIO_RANGE and distance < distanceMin:
                    distanceMin = distance
                    self.airport = ap

    ## Read data of airports from a given file.
    def getAirportDataFile(self, apFile):
        # Check if file exists.
        if not os.path.isfile(apFile):
            self.logger.warning('No such file: {}'.format(apFile))
            return

        # Read the file.
        with open(apFile) as aptInfoFile:
            for li in aptInfoFile:
                lineSplit = re.split('[,;]', li)
                if not li.startswith('#') and len(lineSplit) == 5:
                    self.airportInfos[lineSplit[0].strip()] = (float(
                        lineSplit[1]), float(lineSplit[2]), float(
                            lineSplit[3]), lineSplit[4].replace('\n', ''))

    ## Read data of airports from http://ourairports.com.
    def getAirportDataWeb(self):

        airportFreqs = {}

        # Read the file with frequency.
        with closing(
                urllib2.urlopen(self.OUR_AIRPORTS_URL +
                                'airport-frequencies.csv',
                                timeout=5)) as apFreqFile:
            for li in apFreqFile:
                lineSplit = li.split(',')
                if lineSplit[3] == '"ATIS"':
                    airportFreqs[lineSplit[2].replace('"', '')] = float(
                        lineSplit[-1].replace('\n', ''))

        # Read the file with other aiport data.
        # Add frequency and write them to self. airportInfos.
        with closing(urllib2.urlopen(self.OUR_AIRPORTS_URL +
                                     'airports.csv')) as apFile:
            for li in apFile:
                lineSplit = re.split((",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"), li)

                apCode = lineSplit[1].replace('"', '')
                if apCode in airportFreqs and len(apCode) <= 4:
                    apFreq = airportFreqs[apCode]
                    if 100.0 < apFreq < 140.0:
                        self.airportInfos[apCode] = [
                            apFreq,
                            float(lineSplit[4]),
                            float(lineSplit[5]), lineSplit[3].replace('"', '')
                        ]

    ## Reads airportData from two sources.
    def getAirportData(self):
        self.airportInfos = {}

        try:
            # Try to read airport data from web.
            self.getAirportDataWeb()
            self.getAirportDataFile(
                os.path.join(self.rootDir, 'airports_add.info'))
            collectedFromWeb = True

        except:
            # If this fails, use the airports from airports.info.
            self.logger.warning(
                'Unable to get airport data from web. Using airports.info. Error: {}'
                .format(sys.exc_info()[0]))
            self.airportInfos = {}
            collectedFromWeb = False
            try:
                self.getAirportDataFile(
                    os.path.join(self.rootDir, 'airports.info'))
            except:
                self.logger.error(
                    'Unable to read airport data from airports.info!')

        # Sort airportInfos and write them to a file for future use if collected from web.
        if collectedFromWeb:
            apInfoPath = os.path.join(self.rootDir, 'airports.info')
            apList = self.airportInfos.keys()
            apList.sort()
            with open(apInfoPath, 'w') as apDataFile:
                for ap in apList:
                    apDataFile.write(
                        '{:>4}; {:6.2f}; {:11.6f}; {:11.6f}; {}\n'.format(
                            ap, self.airportInfos[ap][0],
                            self.airportInfos[ap][1], self.airportInfos[ap][2],
                            self.airportInfos[ap][3]))

    ## Determines the info identifier of the loaded ATIS.
    def getInfoIdentifier(self):
        if not self.ivac2:
            informationPos = re.search('information ', self.atisRaw[1]).end()
            informationSplit = self.atisRaw[1][informationPos:].split(' ')
            self.informationIdentifier = informationSplit[0]
        else:
            self.informationIdentifier = CHAR_TABLE[re.findall(
                r'(?<=ATIS )[A-Z](?= \d{4})', self.atisRaw[1])[0]]

    ## Retrieves the metar of an airport independet of an ATIS.
    def getAirportMetar(self):

        if not debug:
            urllib.urlretrieve(self.WHAZZUP_METAR_URL, 'whazzup_metar.txt')

        with open('whazzup_metar.txt', 'r') as metarFile:
            metarText = metarFile.read()

        if not debug:
            os.remove('whazzup_metar.txt')

        metarStart = metarText.find(self.airport)
        metarEnd = metarText.find('\n', metarStart)

        return metarText[metarStart:metarEnd]
Exemple #25
0
 def __init__(self, metar_code, month=None, year=None, utc_delta=None):
     LOGGER.debug(f'creating METAR from: {metar_code}')
     Metar.__init__(self, metar_code, month, year, utc_delta)
     self.press = CustomPressure(self.press.value('mb'))