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 | ++ |
A | LICENSE | | | 18 | ++++++++++++++++++ |
A | Makefile | | | 12 | ++++++++++++ |
A | README.txt | | | 22 | ++++++++++++++++++++++ |
A | gtfs-realtime.proto | | | 638 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | gtrain.c | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | nyct-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;
+}