gtrain

source code for my gtrain tracker project
git clone git://git.alexwennerberg.com/gtrain
Log | Files | Refs | README | LICENSE

commit b4d141ce826a774b60ca8f57fa15eb6be035bdf7
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sat, 11 Mar 2023 12:11:12 -0500

Initial Commit

Importing the libraries, writing makefile, making simple curl requests
and parsing them

Diffstat:
A.gitignore | 2++
ALICENSE | 18++++++++++++++++++
AMakefile | 12++++++++++++
AREADME.txt | 22++++++++++++++++++++++
Agtfs-realtime.proto | 638+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agtrain.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anyct-subway.proto | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 900 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.pb-c* diff --git a/LICENSE b/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2023 alexwennerberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,12 @@ +CC=gcc +CFLAGS = -c -g -std=c99 -I. +LDFLAGS = -L/usr/local/lib +LIBS = -lcurl -lprotobuf-c +TARGET = gtrain +OBJS = gtrain.o + +proto: + protoc-c gtfs-realtime.proto --c_out=. + +$(TARGET) : $(OBJS) + $(CC) -o $(TARGET) gtrain.c gtfs-realtime.pb-c.c $(LDFLAGS) $(LIBS) diff --git a/README.txt b/README.txt @@ -0,0 +1,22 @@ +MTA G Train Tracker +=================== + +This is code for a project I am making to track the Brooklyn G train. + +It isn't really designed to be a generic library, but you could probably copy +the code for your purposes. + +Written in C99 C because why not + +Requires: +- protobuf-c +- libprotobuf-c +- libcurl + +HOW TO USE: +- Get yourself an API key at https://new.mta.info/developers +- Set the API key constant (dont commit it into git) +- make gtrain +- Have Fun + +MIT license with do no evil clause, except when specified diff --git a/gtfs-realtime.proto b/gtfs-realtime.proto @@ -0,0 +1,638 @@ +// Copyright 2015 The GTFS Specifications Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Protocol definition file for GTFS Realtime. +// +// GTFS Realtime lets transit agencies provide consumers with realtime +// information about disruptions to their service (stations closed, lines not +// operating, important delays etc), location of their vehicles and expected +// arrival times. +// +// This protocol is published at: +// https://github.com/google/transit/tree/master/gtfs-realtime + +syntax = "proto2"; + +package transit_realtime; + +option java_package = "com.google.transit.realtime"; + +// The contents of a feed message. +// A feed is a continuous stream of feed messages. Each message in the stream is +// obtained as a response to an appropriate HTTP GET request. +// A realtime feed is always defined with relation to an existing GTFS feed. +// All the entity ids are resolved with respect to the GTFS feed. +// Note that "required" and "optional" as stated in this file refer to Protocol +// Buffer cardinality, not semantic cardinality. See reference.md at +// https://github.com/google/transit/tree/master/gtfs-realtime for field +// semantic cardinality. +message FeedMessage { + // Metadata about this feed and feed message. + required FeedHeader header = 1; + + // Contents of the feed. + repeated FeedEntity entity = 2; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// Metadata about a feed, included in feed messages. +message FeedHeader { + // Version of the feed specification. + // The current version is 2.0. + required string gtfs_realtime_version = 1; + + // Determines whether the current fetch is incremental. Currently, + // DIFFERENTIAL mode is unsupported and behavior is unspecified for feeds + // that use this mode. There are discussions on the GTFS Realtime mailing + // list around fully specifying the behavior of DIFFERENTIAL mode and the + // documentation will be updated when those discussions are finalized. + enum Incrementality { + FULL_DATASET = 0; + DIFFERENTIAL = 1; + } + optional Incrementality incrementality = 2 [default = FULL_DATASET]; + + // This timestamp identifies the moment when the content of this feed has been + // created (in server time). In POSIX time (i.e., number of seconds since + // January 1st 1970 00:00:00 UTC). + optional uint64 timestamp = 3; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// A definition (or update) of an entity in the transit feed. +message FeedEntity { + // The ids are used only to provide incrementality support. The id should be + // unique within a FeedMessage. Consequent FeedMessages may contain + // FeedEntities with the same id. In case of a DIFFERENTIAL update the new + // FeedEntity with some id will replace the old FeedEntity with the same id + // (or delete it - see is_deleted below). + // The actual GTFS entities (e.g. stations, routes, trips) referenced by the + // feed must be specified by explicit selectors (see EntitySelector below for + // more info). + required string id = 1; + + // Whether this entity is to be deleted. Relevant only for incremental + // fetches. + optional bool is_deleted = 2 [default = false]; + + // Data about the entity itself. Exactly one of the following fields must be + // present (unless the entity is being deleted). + optional TripUpdate trip_update = 3; + optional VehiclePosition vehicle = 4; + optional Alert alert = 5; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// +// Entities used in the feed. +// + +// Realtime update of the progress of a vehicle along a trip. +// Depending on the value of ScheduleRelationship, a TripUpdate can specify: +// - A trip that proceeds along the schedule. +// - A trip that proceeds along a route but has no fixed schedule. +// - A trip that have been added or removed with regard to schedule. +// +// The updates can be for future, predicted arrival/departure events, or for +// past events that already occurred. +// Normally, updates should get more precise and more certain (see +// uncertainty below) as the events gets closer to current time. +// Even if that is not possible, the information for past events should be +// precise and certain. In particular, if an update points to time in the past +// but its update's uncertainty is not 0, the client should conclude that the +// update is a (wrong) prediction and that the trip has not completed yet. +// +// Note that the update can describe a trip that is already completed. +// To this end, it is enough to provide an update for the last stop of the trip. +// If the time of that is in the past, the client will conclude from that that +// the whole trip is in the past (it is possible, although inconsequential, to +// also provide updates for preceding stops). +// This option is most relevant for a trip that has completed ahead of schedule, +// but according to the schedule, the trip is still proceeding at the current +// time. Removing the updates for this trip could make the client assume +// that the trip is still proceeding. +// Note that the feed provider is allowed, but not required, to purge past +// updates - this is one case where this would be practically useful. +message TripUpdate { + // The Trip that this message applies to. There can be at most one + // TripUpdate entity for each actual trip instance. + // If there is none, that means there is no prediction information available. + // It does *not* mean that the trip is progressing according to schedule. + required TripDescriptor trip = 1; + + // Additional information on the vehicle that is serving this trip. + optional VehicleDescriptor vehicle = 3; + + // Timing information for a single predicted event (either arrival or + // departure). + // Timing consists of delay and/or estimated time, and uncertainty. + // - delay should be used when the prediction is given relative to some + // existing schedule in GTFS. + // - time should be given whether there is a predicted schedule or not. If + // both time and delay are specified, time will take precedence + // (although normally, time, if given for a scheduled trip, should be + // equal to scheduled time in GTFS + delay). + // + // Uncertainty applies equally to both time and delay. + // The uncertainty roughly specifies the expected error in true delay (but + // note, we don't yet define its precise statistical meaning). It's possible + // for the uncertainty to be 0, for example for trains that are driven under + // computer timing control. + message StopTimeEvent { + // Delay (in seconds) can be positive (meaning that the vehicle is late) or + // negative (meaning that the vehicle is ahead of schedule). Delay of 0 + // means that the vehicle is exactly on time. + optional int32 delay = 1; + + // Event as absolute time. + // In Unix time (i.e., number of seconds since January 1st 1970 00:00:00 + // UTC). + optional int64 time = 2; + + // If uncertainty is omitted, it is interpreted as unknown. + // If the prediction is unknown or too uncertain, the delay (or time) field + // should be empty. In such case, the uncertainty field is ignored. + // To specify a completely certain prediction, set its uncertainty to 0. + optional int32 uncertainty = 3; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features + // and modifications to the spec. + extensions 1000 to 1999; + } + + // Realtime update for arrival and/or departure events for a given stop on a + // trip. Updates can be supplied for both past and future events. + // The producer is allowed, although not required, to drop past events. + message StopTimeUpdate { + // The update is linked to a specific stop either through stop_sequence or + // stop_id, so one of the fields below must necessarily be set. + // See the documentation in TripDescriptor for more information. + + // Must be the same as in stop_times.txt in the corresponding GTFS feed. + optional uint32 stop_sequence = 1; + // Must be the same as in stops.txt in the corresponding GTFS feed. + optional string stop_id = 4; + + optional StopTimeEvent arrival = 2; + optional StopTimeEvent departure = 3; + + // The relation between this StopTime and the static schedule. + enum ScheduleRelationship { + // The vehicle is proceeding in accordance with its static schedule of + // stops, although not necessarily according to the times of the schedule. + // At least one of arrival and departure must be provided. If the schedule + // for this stop contains both arrival and departure times then so must + // this update. + SCHEDULED = 0; + + // The stop is skipped, i.e., the vehicle will not stop at this stop. + // Arrival and departure are optional. + SKIPPED = 1; + + // No data is given for this stop. The main intention for this value is to + // give the predictions only for part of a trip, i.e., if the last update + // for a trip has a NO_DATA specifier, then StopTimes for the rest of the + // stops in the trip are considered to be unspecified as well. + // Neither arrival nor departure should be supplied. + NO_DATA = 2; + } + optional ScheduleRelationship schedule_relationship = 5 + [default = SCHEDULED]; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features + // and modifications to the spec. + extensions 1000 to 1999; + } + + // Updates to StopTimes for the trip (both future, i.e., predictions, and in + // some cases, past ones, i.e., those that already happened). + // The updates must be sorted by stop_sequence, and apply for all the + // following stops of the trip up to the next specified one. + // + // Example 1: + // For a trip with 20 stops, a StopTimeUpdate with arrival delay and departure + // delay of 0 for stop_sequence of the current stop means that the trip is + // exactly on time. + // + // Example 2: + // For the same trip instance, 3 StopTimeUpdates are provided: + // - delay of 5 min for stop_sequence 3 + // - delay of 1 min for stop_sequence 8 + // - delay of unspecified duration for stop_sequence 10 + // This will be interpreted as: + // - stop_sequences 3,4,5,6,7 have delay of 5 min. + // - stop_sequences 8,9 have delay of 1 min. + // - stop_sequences 10,... have unknown delay. + repeated StopTimeUpdate stop_time_update = 2; + + // Moment at which the vehicle's real-time progress was measured. In POSIX + // time (i.e., the number of seconds since January 1st 1970 00:00:00 UTC). + optional uint64 timestamp = 4; + + // The current schedule deviation for the trip. Delay should only be + // specified when the prediction is given relative to some existing schedule + // in GTFS. + // + // Delay (in seconds) can be positive (meaning that the vehicle is late) or + // negative (meaning that the vehicle is ahead of schedule). Delay of 0 + // means that the vehicle is exactly on time. + // + // Delay information in StopTimeUpdates take precedent of trip-level delay + // information, such that trip-level delay is only propagated until the next + // stop along the trip with a StopTimeUpdate delay value specified. + // + // Feed providers are strongly encouraged to provide a TripUpdate.timestamp + // value indicating when the delay value was last updated, in order to + // evaluate the freshness of the data. + // + // NOTE: This field is still experimental, and subject to change. It may be + // formally adopted in the future. + optional int32 delay = 5; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// Realtime positioning information for a given vehicle. +message VehiclePosition { + // The Trip that this vehicle is serving. + // Can be empty or partial if the vehicle can not be identified with a given + // trip instance. + optional TripDescriptor trip = 1; + + // Additional information on the vehicle that is serving this trip. + optional VehicleDescriptor vehicle = 8; + + // Current position of this vehicle. + optional Position position = 2; + + // The stop sequence index of the current stop. The meaning of + // current_stop_sequence (i.e., the stop that it refers to) is determined by + // current_status. + // If current_status is missing IN_TRANSIT_TO is assumed. + optional uint32 current_stop_sequence = 3; + // Identifies the current stop. The value must be the same as in stops.txt in + // the corresponding GTFS feed. + optional string stop_id = 7; + + enum VehicleStopStatus { + // The vehicle is just about to arrive at the stop (on a stop + // display, the vehicle symbol typically flashes). + INCOMING_AT = 0; + + // The vehicle is standing at the stop. + STOPPED_AT = 1; + + // The vehicle has departed and is in transit to the next stop. + IN_TRANSIT_TO = 2; + } + // The exact status of the vehicle with respect to the current stop. + // Ignored if current_stop_sequence is missing. + optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO]; + + // Moment at which the vehicle's position was measured. In POSIX time + // (i.e., number of seconds since January 1st 1970 00:00:00 UTC). + optional uint64 timestamp = 5; + + // Congestion level that is affecting this vehicle. + enum CongestionLevel { + UNKNOWN_CONGESTION_LEVEL = 0; + RUNNING_SMOOTHLY = 1; + STOP_AND_GO = 2; + CONGESTION = 3; + SEVERE_CONGESTION = 4; // People leaving their cars. + } + optional CongestionLevel congestion_level = 6; + + // The degree of passenger occupancy of the vehicle. This field is still + // experimental, and subject to change. It may be formally adopted in the + // future. + enum OccupancyStatus { + // The vehicle is considered empty by most measures, and has few or no + // passengers onboard, but is still accepting passengers. + EMPTY = 0; + + // The vehicle has a relatively large percentage of seats available. + // What percentage of free seats out of the total seats available is to be + // considered large enough to fall into this category is determined at the + // discretion of the producer. + MANY_SEATS_AVAILABLE = 1; + + // The vehicle has a relatively small percentage of seats available. + // What percentage of free seats out of the total seats available is to be + // considered small enough to fall into this category is determined at the + // discretion of the feed producer. + FEW_SEATS_AVAILABLE = 2; + + // The vehicle can currently accommodate only standing passengers. + STANDING_ROOM_ONLY = 3; + + // The vehicle can currently accommodate only standing passengers + // and has limited space for them. + CRUSHED_STANDING_ROOM_ONLY = 4; + + // The vehicle is considered full by most measures, but may still be + // allowing passengers to board. + FULL = 5; + + // The vehicle is not accepting additional passengers. + NOT_ACCEPTING_PASSENGERS = 6; + } + optional OccupancyStatus occupancy_status = 9; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// An alert, indicating some sort of incident in the public transit network. +message Alert { + // Time when the alert should be shown to the user. If missing, the + // alert will be shown as long as it appears in the feed. + // If multiple ranges are given, the alert will be shown during all of them. + repeated TimeRange active_period = 1; + + // Entities whose users we should notify of this alert. + repeated EntitySelector informed_entity = 5; + + // Cause of this alert. + enum Cause { + UNKNOWN_CAUSE = 1; + OTHER_CAUSE = 2; // Not machine-representable. + TECHNICAL_PROBLEM = 3; + STRIKE = 4; // Public transit agency employees stopped working. + DEMONSTRATION = 5; // People are blocking the streets. + ACCIDENT = 6; + HOLIDAY = 7; + WEATHER = 8; + MAINTENANCE = 9; + CONSTRUCTION = 10; + POLICE_ACTIVITY = 11; + MEDICAL_EMERGENCY = 12; + } + optional Cause cause = 6 [default = UNKNOWN_CAUSE]; + + // What is the effect of this problem on the affected entity. + enum Effect { + NO_SERVICE = 1; + REDUCED_SERVICE = 2; + + // We don't care about INsignificant delays: they are hard to detect, have + // little impact on the user, and would clutter the results as they are too + // frequent. + SIGNIFICANT_DELAYS = 3; + + DETOUR = 4; + ADDITIONAL_SERVICE = 5; + MODIFIED_SERVICE = 6; + OTHER_EFFECT = 7; + UNKNOWN_EFFECT = 8; + STOP_MOVED = 9; + } + optional Effect effect = 7 [default = UNKNOWN_EFFECT]; + + // The URL which provides additional information about the alert. + optional TranslatedString url = 8; + + // Alert header. Contains a short summary of the alert text as plain-text. + optional TranslatedString header_text = 10; + + // Full description for the alert as plain-text. The information in the + // description should add to the information of the header. + optional TranslatedString description_text = 11; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features + // and modifications to the spec. + extensions 1000 to 1999; +} + +// +// Low level data structures used above. +// + +// A time interval. The interval is considered active at time 't' if 't' is +// greater than or equal to the start time and less than the end time. +message TimeRange { + // Start time, in POSIX time (i.e., number of seconds since January 1st 1970 + // 00:00:00 UTC). + // If missing, the interval starts at minus infinity. + optional uint64 start = 1; + + // End time, in POSIX time (i.e., number of seconds since January 1st 1970 + // 00:00:00 UTC). + // If missing, the interval ends at plus infinity. + optional uint64 end = 2; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// A position. +message Position { + // Degrees North, in the WGS-84 coordinate system. + required float latitude = 1; + + // Degrees East, in the WGS-84 coordinate system. + required float longitude = 2; + + // Bearing, in degrees, clockwise from North, i.e., 0 is North and 90 is East. + // This can be the compass bearing, or the direction towards the next stop + // or intermediate location. + // This should not be direction deduced from the sequence of previous + // positions, which can be computed from previous data. + optional float bearing = 3; + + // Odometer value, in meters. + optional double odometer = 4; + // Momentary speed measured by the vehicle, in meters per second. + optional float speed = 5; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// A descriptor that identifies an instance of a GTFS trip, or all instances of +// a trip along a route. +// - To specify a single trip instance, the trip_id (and if necessary, +// start_time) is set. If route_id is also set, then it should be same as one +// that the given trip corresponds to. +// - To specify all the trips along a given route, only the route_id should be +// set. Note that if the trip_id is not known, then stop sequence ids in +// TripUpdate are not sufficient, and stop_ids must be provided as well. In +// addition, absolute arrival/departure times must be provided. +message TripDescriptor { + // The trip_id from the GTFS feed that this selector refers to. + // For non frequency-based trips, this field is enough to uniquely identify + // the trip. For frequency-based trip, start_time and start_date might also be + // necessary. + optional string trip_id = 1; + + // The route_id from the GTFS that this selector refers to. + optional string route_id = 5; + + // The direction_id from the GTFS feed trips.txt file, indicating the + // direction of travel for trips this selector refers to. This field is + // still experimental, and subject to change. It may be formally adopted in + // the future. + optional uint32 direction_id = 6; + + // The initially scheduled start time of this trip instance. + // When the trip_id corresponds to a non-frequency-based trip, this field + // should either be omitted or be equal to the value in the GTFS feed. When + // the trip_id corresponds to a frequency-based trip, the start_time must be + // specified for trip updates and vehicle positions. If the trip corresponds + // to exact_times=1 GTFS record, then start_time must be some multiple + // (including zero) of headway_secs later than frequencies.txt start_time for + // the corresponding time period. If the trip corresponds to exact_times=0, + // then its start_time may be arbitrary, and is initially expected to be the + // first departure of the trip. Once established, the start_time of this + // frequency-based trip should be considered immutable, even if the first + // departure time changes -- that time change may instead be reflected in a + // StopTimeUpdate. + // Format and semantics of the field is same as that of + // GTFS/frequencies.txt/start_time, e.g., 11:15:35 or 25:15:35. + optional string start_time = 2; + // The scheduled start date of this trip instance. + // Must be provided to disambiguate trips that are so late as to collide with + // a scheduled trip on a next day. For example, for a train that departs 8:00 + // and 20:00 every day, and is 12 hours late, there would be two distinct + // trips on the same time. + // This field can be provided but is not mandatory for schedules in which such + // collisions are impossible - for example, a service running on hourly + // schedule where a vehicle that is one hour late is not considered to be + // related to schedule anymore. + // In YYYYMMDD format. + optional string start_date = 3; + + // The relation between this trip and the static schedule. If a trip is done + // in accordance with temporary schedule, not reflected in GTFS, then it + // shouldn't be marked as SCHEDULED, but likely as ADDED. + enum ScheduleRelationship { + // Trip that is running in accordance with its GTFS schedule, or is close + // enough to the scheduled trip to be associated with it. + SCHEDULED = 0; + + // An extra trip that was added in addition to a running schedule, for + // example, to replace a broken vehicle or to respond to sudden passenger + // load. + ADDED = 1; + + // A trip that is running with no schedule associated to it, for example, if + // there is no schedule at all. + UNSCHEDULED = 2; + + // A trip that existed in the schedule but was removed. + CANCELED = 3; + } + optional ScheduleRelationship schedule_relationship = 4; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// Identification information for the vehicle performing the trip. +message VehicleDescriptor { + // Internal system identification of the vehicle. Should be unique per + // vehicle, and can be used for tracking the vehicle as it proceeds through + // the system. + optional string id = 1; + + // User visible label, i.e., something that must be shown to the passenger to + // help identify the correct vehicle. + optional string label = 2; + + // The license plate of the vehicle. + optional string license_plate = 3; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// A selector for an entity in a GTFS feed. +message EntitySelector { + // The values of the fields should correspond to the appropriate fields in the + // GTFS feed. + // At least one specifier must be given. If several are given, then the + // matching has to apply to all the given specifiers. + optional string agency_id = 1; + optional string route_id = 2; + // corresponds to route_type in GTFS. + optional int32 route_type = 3; + optional TripDescriptor trip = 4; + optional string stop_id = 5; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} + +// An internationalized message containing per-language versions of a snippet of +// text or a URL. +// One of the strings from a message will be picked up. The resolution proceeds +// as follows: +// 1. If the UI language matches the language code of a translation, +// the first matching translation is picked. +// 2. If a default UI language (e.g., English) matches the language code of a +// translation, the first matching translation is picked. +// 3. If some translation has an unspecified language code, that translation is +// picked. +message TranslatedString { + message Translation { + // A UTF-8 string containing the message. + required string text = 1; + // BCP-47 language code. Can be omitted if the language is unknown or if + // no i18n is done at all for the feed. At most one translation is + // allowed to have an unspecified language tag. + optional string language = 2; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; + } + // At least one translation must be provided. + repeated Translation translation = 1; + + // The extensions namespace allows 3rd-party developers to extend the + // GTFS Realtime Specification in order to add and evaluate new features and + // modifications to the spec. + extensions 1000 to 1999; +} diff --git a/gtrain.c b/gtrain.c @@ -0,0 +1,73 @@ +// some of this stolen from https://curl.se/libcurl/c/getinmemory.html etc + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <curl/curl.h> +#include <protobuf-c/protobuf-c.h> + +#include "gtfs-realtime.pb-c.h" + +int stations[21]; + +// From https://api.mta.info/#/subwayRealTimeFeeds +const char GTRAIN_URL[] = "https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-g"; +const char API_KEYH[] = "x-api-key: setme"; + + +struct MemoryStruct { + char *memory; + size_t size; +}; + +static size_t +WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + char *ptr = realloc(mem->memory, mem->size + realsize + 1); + if(!ptr) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + return realsize; +} + +void getLatestGData(struct MemoryStruct *chunk) { + CURL *curl; + CURLcode res; + struct curl_slist* headers = NULL; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, GTRAIN_URL); + headers = curl_slist_append(headers, API_KEYH); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk); + res = curl_easy_perform(curl); + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + curl_easy_cleanup(curl); +} + +int main() { + struct MemoryStruct chunk; + chunk.memory = malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + getLatestGData(&chunk); + + TransitRealtime__FeedMessage *msg; + msg = transit_realtime__feed_message__unpack( + NULL, + chunk.size, + chunk.memory); + printf("%s", msg->header->gtfs_realtime_version); + free(chunk.memory); +} diff --git a/nyct-subway.proto b/nyct-subway.proto @@ -0,0 +1,135 @@ +// +// NYCT Subway extensions for the GTFS-realtime protocol. +// From https://api.mta.info/nyct-subway.proto.txt +// +option java_package = "com.google.transit.realtime"; + +import "gtfs-realtime.proto"; + +message TripReplacementPeriod { + // The replacement period is for this route + optional string route_id = 1; + // The start time is omitted, the end time is currently now + 30 minutes for + // all routes of the A division + optional transit_realtime.TimeRange replacement_period = 2; +} + +// NYCT Subway extensions for the feed header +message NyctFeedHeader { + // Version of the NYCT Subway extensions + // The current version is 1.0 + required string nyct_subway_version = 1; + // For the NYCT Subway, the GTFS-realtime feed replaces any scheduled + // trip within the trip_replacement_period. + // This feed is a full dataset, it contains all trips starting + // in the trip_replacement_period. If a trip from the static GTFS is not + // found in the GTFS-realtime feed, it should be considered as cancelled. + // The replacement period can be different for each route, so here is + // a list of the routes where the trips in the feed replace all + // scheduled trips within the replacement period. + repeated TripReplacementPeriod trip_replacement_period = 2; +} + +extend transit_realtime.FeedHeader { + optional NyctFeedHeader nyct_feed_header = 1001; +} + +// NYCT Subway extensions for the trip descriptor +message NyctTripDescriptor { + // The nyct_train_id is meant for internal use only. It provides an + // easy way to associated GTFS-realtime trip identifiers with NYCT rail + // operations identifier + // + // The ATS office system assigns unique train identification (Train ID) to + // each train operating within or ready to enter the mainline of the + // monitored territory. An example of this is 06 0123+ PEL/BBR and is decoded + // as follows: + // + // The first character represents the trip type designator. 0 identifies a + // scheduled revenue trip. Other revenue trip values that are a result of a + // change to the base schedule include; [= reroute], [/ skip stop], [$ turn + // train] also known as shortly lined service. + // + // The second character 6 represents the trip line i.e. number 6 train The + // third set of characters identify the decoded origin time. The last + // character may be blank "on the whole minute" or + "30 seconds" + // + // Note: Origin times will not change when there is a trip type change. This + // is followed by a three character "Origin Location" / "Destination + // Location" + optional string train_id = 1; + + // This trip has been assigned to a physical train. If true, this trip is + // already underway or most likely will depart shortly. + // + // Train Assignment is a function of the Automatic Train Supervision (ATS) + // office system used by NYCT Rail Operations to monitor and track train + // movements. ATS provides the ability to "assign" the nyct_train_id + // attribute when a physical train is at its origin terminal. These assigned + // trips have the is_assigned field set in the TripDescriptor. + // + // When a train is at a terminal but has not been given a work program it is + // declared unassigned and is tagged as such. Unassigned trains can be moved + // to a storage location or assigned a nyct_train_id when a determination for + // service is made. + optional bool is_assigned = 2; + + // The direction the train is moving. + enum Direction { + NORTH = 1; + EAST = 2; + SOUTH = 3; + WEST = 4; + } + // Uptown and Bronx-bound trains are moving NORTH. + // Times Square Shuttle to Grand Central is also northbound. + // + // Downtown and Brooklyn-bound trains are moving SOUTH. + // Times Square Shuttle to Times Square is also southbound. + // + // EAST and WEST are not used currently. + optional Direction direction = 3; +} + +extend transit_realtime.TripDescriptor { + optional NyctTripDescriptor nyct_trip_descriptor = 1001; +} + +// NYCT Subway extensions for the stop time update +message NyctStopTimeUpdate { + // Provides the planned station arrival track. The following is the Manhattan + // track configurations: + // 1: southbound local + // 2: southbound express + // 3: northbound express + // 4: northbound local + // + // In the Bronx (except Dyre Ave line) + // M: bi-directional express (in the AM express to Manhattan, in the PM + // express away). + // + // The Dyre Ave line is configured: + // 1: southbound + // 2: northbound + // 3: bi-directional + optional string scheduled_track = 1; + + // This is the actual track that the train is operating on and can be used to + // determine if a train is operating according to its current schedule + // (plan). + // + // The actual track is known only shortly before the train reaches a station, + // typically not before it leaves the previous station. Therefore, the NYCT + // feed sets this field only for the first station of the remaining trip. + // + // Different actual and scheduled track is the result of manually rerouting a + // train off it scheduled path. When this occurs, prediction data may become + // unreliable since the train is no longer operating in accordance to its + // schedule. The rules engine for the 'countdown' clocks will remove this + // train from all schedule stations. + optional string actual_track = 2; +} + +extend transit_realtime.TripUpdate.StopTimeUpdate { + optional NyctStopTimeUpdate nyct_stop_time_update = 1001; +}