資料介紹
描述
在隔離期間我發(fā)現(xiàn)自己在家的額外時間里,我最近在他們的開發(fā)者門戶上發(fā)現(xiàn)了谷歌的交通 API。這些 API 提供了實時跟蹤美國大城市公共交通的掛鉤。許多主要城市的交通系統(tǒng)都利用了這一點,并以谷歌的通用交通信息規(guī)范 (GTFS) 格式提供火車、公共汽車等的實時跟蹤信息。
GTFS 是一種用于公共交通時刻表和相關地理信息的開放數(shù)據(jù)格式。GTFS 有一個名為 GTFS Realtime 的擴展,它是一個供公共交通機構(gòu)向應用程序開發(fā)人員提供有關其車隊的實時更新的提要。紐約市大都會交通管理局 (MTA) 為其每條地鐵線路創(chuàng)建了 GTFS 實時提要,這些提要用于地鐵站的實時倒計時顯示,直到下一趟火車,也用于 GPS 應用程序(如谷歌地圖)以幫助計算用戶在公共交通工具上請求兩個位置之間的路線時的總行程時間。MTA 大約每 30 秒用新數(shù)據(jù)更新這些提要。
作為大流行前的紐約人,我早上通勤中最令人沮喪的部分是在我的火車上門即將關閉時走進車站,然后不得不等待 20 多分鐘才能到達下一站(在布魯克林)。作為大流行后的紐約人,一旦我的辦公室重新開放并且我不再在家工作,我非常想盡可能地限制我在火車站度過的時間。
由于我的 Ultra96 已經(jīng)與我的各種其他 SDR Web 應用程序一起在我的家庭網(wǎng)絡上建立了自己的永久固定裝置,我決定添加另一個 Web 應用程序,我可以在我的手機上調(diào)用它來查詢 A/C/E 線路的實時饋送和解析出 C 線的特定警報和跟蹤信息,這樣我就可以在我離開公寓的時間與北行 C 線列車到達我當?shù)剀囌镜臅r間相匹配。
考慮到項目構(gòu)想,在 Ultra96 FPGA 開發(fā)板上開發(fā) Web 應用程序的主要步驟:
- 創(chuàng)建自定義項目(通常是 Python 腳本)。
- 創(chuàng)建將自定義項目集成到 Ultra96 的網(wǎng)絡服務器中的 Web 應用程序 Python 后端。
- 創(chuàng)建 Web 應用程序 HTML 前端作為項目的用戶界面網(wǎng)頁。
要訪問地鐵線路的這些實時信息,MTA 需要從其開發(fā)人員門戶生成的 API 密鑰。這是為了將任何提要在任何時間點看到的流量保持在合理的水平。為了遵守 MTA 的使用指南,重要的是要注意這個項目在 Ultra96 的網(wǎng)絡服務器上而不是在 MTA 的服務器上運行,它絕不是 MTA 許可的,不能保證準確/及時,最重要的是這個項目是純娛樂。如果您計劃使用 MTA 的實時供稿重新創(chuàng)建/擴展此項目,請確保您了解 MTA 的使用規(guī)則和指南。
在描述我是如何在 Ultra96 上設置我的 Web 應用程序以查詢這些實時提要之前的一些背景知識,GTFS Realtime 基于協(xié)議緩沖區(qū)(proto2) 語法。協(xié)議緩沖區(qū)語言最初是由Google開發(fā)的,作為存儲和交換結(jié)構(gòu)化信息的優(yōu)化方法。它使用接口描述語言(IDL) 來描述給定數(shù)據(jù)集的結(jié)構(gòu),然后它實現(xiàn)一個程序,該程序從該描述生成源代碼,然后用于生成或解析表示結(jié)構(gòu)化數(shù)據(jù)的字節(jié)流。總的來說,與將數(shù)據(jù)序列化為 XML 或蟒蛇酸洗。
GTFS 實時提要包含三個主要的提要實體(又名數(shù)據(jù)類型):行程更新、服務警報和車輛位置。這些提要實體可以以任何所需的方式組合以創(chuàng)建自定義提要。MTA 以這種方式創(chuàng)建了自己的提要,以將自己的自定義擴展添加到 GTFS Realtime。提要通過 HTTP 提供,如我之前提到的,每隔 30 秒更新一次,并且由于提要的輸出文件是常規(guī)的二進制文件,因此任何類型的網(wǎng)絡服務器都可以托管和提供該文件。這意味著Ultra96的基于 Flask 的網(wǎng)絡服務器可以發(fā)送有效的 HTTP GET 請求以返回供在 Ultra96 上處理的提要數(shù)據(jù)。
New York City Transit Subway 有 12 個提要,用于與地鐵列車的運動/狀態(tài)相關的所有各種數(shù)據(jù)集:FeedMessage、FeedHeader、NyctFeedHeader、TripReplacementPeriod、TripUpdate、TripDescriptor、NyctTripDescriptor、StopTimeUpdate、NyctStopTimeUpdate、StopTimeEvent、VehiclePosition 和警報。對于這個項目,我選擇使用 FeedMessage 提要,因為它是任何其他 NYCT (NYC Transit) 擴展提要(NyctFeedHeader、NyctTripDescriptor 和 NyctStopTimeUpdate)引用的完整數(shù)據(jù)集。有關 12 個提要中每一個提要的說明,請參閱 MTA 開發(fā)人員門戶網(wǎng)站上題為“紐約市地鐵的 GTFS 實時參考”的附件文檔。
Ultra96 上的自定義項目將是一個 Python 腳本,用于查詢 A/C/E 地鐵線路的 MTA 實時饋送,并在運行時返回用戶指定的車站的曼哈頓方向(北行)C 列車的到達時間。相應的自定義網(wǎng)頁將獲取此到達時間數(shù)組并為用戶列出它們,以便用戶不僅可以看到當前設置下一班火車到達的時間,還可以看到當前在 C 上運行的火車的所有后續(xù)到達時間尚未停在指定車站的線路。
當查詢 A/C/E 行的實時提要時,它會返回所有三行的數(shù)據(jù),直到 Ultra96 上的 Python 腳本對其進行解析,然后僅將所需的數(shù)據(jù)傳遞給網(wǎng)頁。目前,我已對其進行硬編碼,僅查找前往曼哈頓的 C 列車的行程更新實體,因為我住在布魯克林足夠遠的地方,以至于我很少從當?shù)剀囌境俗剪斂肆?南開的 C 列車。
總體而言,在開始開發(fā) Ultra96 之前,需要從 MTA 的開發(fā)人員門戶獲取兩件事:用于 A/C/E 行的實時提要的鏈接,以及能夠查詢提要的 API 訪問密鑰。在此處創(chuàng)建 MTA 開發(fā)者帳戶并登錄后,我在屏幕頂部的“訪問密鑰”菜單選項下生成了自己的 API 訪問密鑰,我被要求填寫一些基本聯(lián)系信息以換取密鑰(在如果違反使用條款,MTA 將停用密鑰)。然后在 Feeds 菜單選項和 Subway Realtime Feeds 子菜單下,我復制了 A/C/E 行的鏈接。
要開始使用 Ultra96 本身,需要使用 Python 包管理器 pip 安裝 Python 的 GTFS 實時綁定模塊:
pip3 install --upgrade gtfs-realtime-bindings
雖然用于 HTTP 客戶端和發(fā)送 HTTP/1.1 請求的 Python 模塊已經(jīng)安裝在用于 Web 服務器的 Ultra96 上,但我發(fā)現(xiàn)它們需要升級/更新才能使其與 GTFS Realtime 一起使用:
pip3 install --upgrade urllib
pip3 install --upgrade requests
在 Ultra96 的自定義內(nèi)容網(wǎng)頁上,我創(chuàng)建了一個新的自定義項目,它打開 Ultra96 上的文本編輯器來編寫 Python 腳本。由主函數(shù)調(diào)用的核心函數(shù) c_train() 使用我唯一的 API 密鑰向 MTA 實時提要發(fā)送一個簡單的 GET 請求。返回提要后,腳本會解析每個實體,尋找 C 列火車的 route_id 標簽。通過檢查trip_id 中的“N”標簽來進一步過濾這些實體中的每一個,以表明它是北行列車。這些提要實體中的每一個都包含一個 StopTimeUpdate 子實體,其中包含一組數(shù)據(jù),這些數(shù)據(jù)對應于火車尚未??康乃熊囌疽约霸摶疖囋谶@些車站的當前估計到達時間。每個站都有一個 stop_id 標簽,我在這個網(wǎng)站上找到了整個列表將每個 stop_ids 轉(zhuǎn)換為相應的站名(我為此創(chuàng)建了子函數(shù) stationId_to_stationName())。
就個人而言,我發(fā)現(xiàn)與其列出每列火車當前到達的具體時間,倒計時時間列表對我來說更清楚一些。因此,例如,不要顯示下一班火車將在下午 2:30 到達,而是顯示下一班火車將在 25 分鐘后到達。這就是倒計時時鐘顯示當前在車站本身中的工作方式,所以也許這就是我偏向于這種輸出數(shù)據(jù)格式的原因。該腳本簡單地通過使用 datetime Python 模塊查詢當前時間從時間戳計算倒計時時間,然后從估計的到達時間中減去當前時間,以獲得用作倒計時到達時間的時間增量。這些倒計時到達時間的數(shù)組被返回給主函數(shù)。
地鐵跟蹤項目Python代碼:
import os
import re
import sys
import time
import pytz
import json
import urllib
import requests
import datetime
import subprocess
from pytz import timezone
from google.transit import gtfs_realtime_pb2
def stationId_to_stationName(stationId):
if (stationId == "A09N"):
stationName = "168 St"
elif (stationId == "A10N"):
stationName = "163 St - Amsterdam Av"
elif (stationId == "A11N"):
stationName = "155 St"
elif (stationId == "A12N"):
stationName = "145 St"
elif (stationId == "A14N"):
stationName = "135 St"
elif (stationId == "A15N"):
stationName = "125 St"
elif (stationId == "A16N"):
stationName = "116 St"
elif (stationId == "A17N"):
stationName = "Cathedral Pkwy - 110 St"
elif (stationId == "A18N"):
stationName = "118 St"
elif (stationId == "A19N"):
stationName = "96 St"
elif (stationId == "A20N"):
stationName = "86 St"
elif (stationId == "A21N"):
stationName = "81 St - Museum of Natural History"
elif (stationId == "A22N"):
stationName = "72 St"
elif (stationId == "A24N"):
stationName = "59 St - Columbus Circle"
elif (stationId == "A25N"):
stationName = "50 St"
elif (stationId == "A27N"):
stationName = "42 St - Port Authority Bus Terminal"
elif (stationId == "A28N"):
stationName = "34 St - Penn Station"
elif (stationId == "A30N"):
stationName = "23 St"
elif (stationId == "A31N"):
stationName = "14 St"
elif (stationId == "A32N"):
stationName = "W 4 St - Wash Sq"
elif (stationId == "A33N"):
stationName = "Spring St"
elif (stationId == "A34N"):
stationName = "Canal St"
elif (stationId == "A36N"):
stationName = "Chambers St"
elif (stationId == "A38N"):
stationName = "Fulton St"
elif (stationId == "A40N"):
stationName = "High St"
elif (stationId == "A41N"):
stationName = "Jay St - MetroTech"
elif (stationId == "A42N"):
stationName = "Hoyt - Schermerhorn Sts"
elif (stationId == "A43N"):
stationName = "Lafayette Av"
elif (stationId == "A44N"):
stationName = "Clinton-Washington Avs"
elif (stationId == "A45N"):
stationName = "Franklin Av"
elif (stationId == "A46N"):
stationName = "Nostrand Av"
elif (stationId == "A47N"):
stationName = "Kingston - Throop Avs"
elif (stationId == "A48N"):
stationName = "Utica Av"
elif (stationId == "A49N"):
stationName = "Ralph Av"
elif (stationId == "A50N"):
stationName = "Rockaway Av"
elif (stationId == "A51N"):
stationName = "Broadway Jct"
elif (stationId == "A52N"):
stationName = "Liberty Av"
elif (stationId == "A53N"):
stationName = "Van Siclen Av"
elif (stationId == "A54N"):
stationName = "Shepherd Av"
elif (stationId == "A55N"):
stationName = "Euclid Av"
else:
stationName = "Invalid C line station ID"
#print(stationName)
return stationName
def c_train(StationIdRequested):
#this just sets a default station in case passed argument is null
if (StationIdRequested == ""):
StationIdRequested == "A44N"
headers = {
"x-api-key": 'YOUR GENERATED API KEY FROM THE MTA HERE'
}
feed = gtfs_realtime_pb2.FeedMessage()
response = urllib.request.Request('https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-ace', headers=headers)
xml = urllib.request.urlopen(response)
feed.ParseFromString(xml.read())
arrival_times = []
for entity in feed.entity:
if (entity.trip_update.trip.route_id == "C"):
stopCntr = 0
dir_str = entity.trip_update.trip.trip_id
direction = dir_str.find("N")
if (direction != -1):
for x in entity.trip_update.stop_time_update:
StationId = entity.trip_update.stop_time_update[stopCntr].stop_id
if (StationId == StationIdRequested):
# Train has yet to stop at Clinton-Washington Avs
stationArr = entity.trip_update.stop_time_update[stopCntr].arrival
stationDpt = entity.trip_update.stop_time_update[stopCntr].departure
stationName = stationId_to_stationName(StationId)
arrString = str(stationArr)
arrNum = re.findall(r'\d+', arrString)
arrFloat = float(arrNum[0])
dptString = str(stationDpt)
dptNum = re.findall(r'\d+', dptString)
dptFloat = float(dptNum[0])
arrivalTime = datetime.datetime.utcfromtimestamp(arrFloat)
departTime = datetime.datetime.utcfromtimestamp(dptFloat)
currTime = datetime.datetime.now(datetime.timezone.utc)
currTimeNaive = currTime.replace(tzinfo=None)
time_delta = arrivalTime - currTimeNaive
total_seconds = time_delta.total_seconds()
minutes = total_seconds/60
#print statement for debugging purposes
#print('Manhattan bound C train:', entity.trip_update.trip.trip_id, 'arriving at', stationName,'in', minutes, 'minutes')
arrival_times.append(minutes)
stopCntr = stopCntr + 1
return arrival_times
def main(arg):
arrivalTimes = c_train(arg)
print(arrivalTimes)
if __name__ == "__main__":
main(sys.argv[1])
返回到自定義內(nèi)容主網(wǎng)頁,我選擇了編輯 Webapp 選項,然后為運行項目腳本的自定義網(wǎng)頁創(chuàng)建了一個新的后端和前端。需要注意的是,前端和后端文件的文件名必須相同,這樣 Ultra96 上的模板才能正確生成新的 webserver Python 腳本。
Python 后端將負責處理前端的請求,并將請求的站 ID 作為參數(shù)傳遞給 Python 腳本的 main 函數(shù)。項目腳本拉取并解析C行倒計時到達時間,然后返回到后端,后端將數(shù)據(jù)傳遞給前端顯示給用戶。
Python后端代碼:
@app.route("/c_train_tracking.html", methods=["GET", "POST"])
def c_train_tracking():
if request.method == "POST":
station = request.form.get("stations", None)
if station!=None:
proc = subprocess.Popen('python3 /usr/share/ultra96-startup-pages/webapp/templates/CustomContent/custom/c_train_tracking.py '+station+'' ,stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
output,err = proc.communicate()
if(station == "A41N"):
name = "Jay St - MetroTech"
elif(station == "A42N"):
name = "Hoyt - Schermerhorn Sts"
elif(station == "A43N"):
name = "Lafayette Av"
elif(station == "A44N"):
name = "Clinton - Washington Avs"
return render_template("CustomContent/custom_front_end/c_train_tracking.html", output=output, station=station, name=name)
return render_template("CustomContent/custom_front_end/c_train_tracking.html")
HTML 前端用作用戶界面,下拉菜單允許用戶選擇所需的車站以查詢所有未來的火車倒計時到到達時間(以分鐘為單位)(是的,我確實計劃編寫 C 火車的其余部分就像我在項目 Python 腳本中所做的那樣,將來會在此處駐留)。
HTML前端代碼:
{% extends "Default/default.html" %}
{% block content %}
<div class="page-header">
<h1 class="display-4"><b>{% block title %}C Train Tracker{% endblock %}b>h1>
div>
<h1>Station Wait Times for Manhattan-bound C Trainsh1>
<meta http-equiv="explore" content="B" />
<p>Select station from drop down:p>
<form id="form1" action="/c_train_tracking.html" method="POST" enctype="multipart/form-data">
<select id="stations_dropdown" name="stations">
<option disabled="disabled" selected="selected" value="A44N">Select Stationoption>
<option value="A41N">Jay St - MetroTechoption>
<option value="A42N">Hoyt - Schermerhorn Stsoption>
<option value="A43N">Lafayette Avoption>
<option value="A44N">Clinton - Washington Avsoption>
select>
<input type="submit" value="Submit">
form>
<br><br>
<h2>Arrival Times for {{ name }}h2>
<p id="times">p>
<script>
var myObj, i, x = "";
myObj = {
"arrivalTimes":{{ output }}
};
for (i in myObj.arrivalTimes) {
x += myObj.arrivalTimes[i] + " minutes
";
}
document.getElementById("times").innerHTML = x;
script>
{% endblock %}
完成前端和后端并返回到 Reload Webapp 頁面后,我選中了包含我的新頁面的選項,然后單擊按鈕重新加載 webapp。
一旦 Ultra96 重新啟動并連接回我的 Wi-Fi,這是我第一次查詢下拉菜單中的一個站的結(jié)果:
成功。
當一個項目變得既實用又有趣時,這真是太棒了,我很高興在客人連接到我的 Wi-Fi 時向他們發(fā)送鏈接(只要生活足夠正常,朋友們可以再次訪問)當他們準備回家時使用它們來計時從我的公寓步行到當?shù)剀囌镜臅r間。尤其是在傍晚時分,火車改成更零星的夜間時間表。
這也將為我在隔離后的早晨例行工作提供一些額外的結(jié)構(gòu),因為我將有辦法準確衡量我需要多慢或多快才能在車站趕上火車。
我發(fā)現(xiàn)的唯一問題是,由于 MTA 大約每 30 秒更新一次提要,因此運行底層項目 Python 腳本來查詢提要可能需要大約相同的時間。所以我注意到每隔一段時間,在點擊所選電臺的提交按鈕后,到達時間刷新最多需要 30 秒。
這個項目有很大的擴展空間。最終,我將使硬編碼參數(shù)也可配置,例如方向(北行與南行)以及查詢哪列火車(特別是因為快車 A 火車在深夜接管了當?shù)?C 火車的??空荆?。直到下一次....
- Ultra96硬件用戶指南
- Ultra96 SDR第一部分:簡單的射頻頻譜圖Web應用程序
- Ultra96 CSI-2視頻輸出到Raspberry Pi攝像頭輸入
- Ultra96上的實時攝像頭饋送網(wǎng)頁
- 使用PYNQ的Ultra96面部識別鎖栓
- 使用Tensil、TF-Lite和PYNQ在Ultra96板上運行YOLO v4 Tiny
- 在Ultra96 V2平臺上用Python實現(xiàn)人臉檢測和人臉跟蹤
- 用于Ultra96的夾層板96AnalogXperience
- 關于Ultra96的Xilinx DDS編譯器IP教程
- 與Ultra96聯(lián)網(wǎng)端口轉(zhuǎn)發(fā)
- Ultra96 V2上基于標記的增強現(xiàn)實
- 使用Ultra96 PYNQ測定織物GSM
- Ultra96皮膚癌AI構(gòu)建
- 2018.2 Ultra96:從 Matchbox 桌面關斷 PetaLinux BSP,無法關斷電路板
- 一起玩Ultra96之GPIO操作
- 【從0開始創(chuàng)建AWTK應用程序】創(chuàng)建應用程序并在模擬器運行 356次閱讀
- SEW-MOVIPRO啟動應用程序配置程序 605次閱讀
- PreEmptiveProtection:全面的移動應用程序保護 529次閱讀
- 怎么使用uboot引導應用程序? 1360次閱讀
- 聊聊PHP的web應用程序開發(fā)框架存在的漏洞有哪些 1562次閱讀
- 在MPSoC ZCU106單板的HDMI-Tx上基于eglfs_kms的運行QT應用程序 2773次閱讀
- 如何在OCI Ampere A1上啟動計算密集AI應用程序 1777次閱讀
- DPU編譯應用程序的不同方法 1579次閱讀
- 淺談RAM 執(zhí)行應用程序 3060次閱讀
- HarmonyOS應用程序Ability的作用 2391次閱讀
- Zynq-7000的256KB OCM應用程序運行 2711次閱讀
- 應用程序安全怎樣來提供保障 1145次閱讀
- 如何在谷歌云上使用Hyperledger Caliper測試區(qū)塊鏈應用程序 1212次閱讀
- 基于Arm技術的16nm MPSoC開發(fā)套件Ultra96 6099次閱讀
- 添加FPGA終端的步驟方法 2346次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數(shù)字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅(qū)動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多