B1 | B1 Portable App | TrackMan Data Feeds: Live Play-by-Play Feed for Portable B1

Please read this article for an overview of the various TrackMan APIs available across our product stack: Trackman Data API Introduction for Stadium v3 and Portable B1

 

This article covers:

Objective

Customer responsibilities and environment

Sequence of Events

SubscriptionValidation Message

Session Message

Ball Message

 

Objective

Enable a custom application to receive ball tracking measurements in “semi real-time” for each pitch/hit tracked by the Trackman B1 system. The goal is to make data available to the custom application within 3 (three) seconds after data is available on the iPad App managing the B1 system; a faster than 3-second latency may be possible in certain settings.

 

This Feed *only* publishes ball tracking data and no metadata (player name, tagging, etc). The message format is intentionally kept compatible with (and a subset of) the message format from the TrackMan Stadium system so a customer can re-use code across both systems.

 

Customer responsibilities and environment

The following must be implemented and provided by the customer:

  1. Two endpoints reachable on the internet that adhere to the webhook protocol. Both endpoints must respond to an incoming SubscriptionValidation message (see later on this page). The webhook can be implemented in any language.

  2. Provide URL for the two webhooks to TrackMan support who will then update the customer's TrackMan Portable system configuration:
    1. Webhook for session start
    2. Webhook for ball events.
  3. The iPad with the TrackMan App must have internet connectivity during a session:

    1. EITHER by providing a Wi-Fi access point with internet connectivity and allowing both the TrackMan B1 and the iPad to connect to this access point. See this FAQ for how to re-configure your B1: Connect the B1 to an existing Wi-Fi Access Point or via Ethernet

    2. OR by using an iPad with 5G/LTE cellular connection to enable the iPad to communicate with the B1 via WiFi while simultaneously connecting to the internet via 4G/LTE.

Sequence of Events

The TrackMan Baseball App running on an iPad will be used to calibrate the B1, start a pitching/batting session and track each pitch or hit including tagging with player metadata. These events will be sent to the customer webhook:

  1. When configuring the webhook: TrackMan calls each webhook with a SubscriptionValidation message.
  2. At session start (user completes "start session" in the App) TrackMan will call the webhook with a session message containing

    1. A unique session identifier

    2. Location (free text) as entered by the user during session flow in the App:
      MicrosoftTeams-image__3_.png

  3. For each new ball tracking event, TrackMan will call the webhook with a ball message containing tracking measurements (pitch or hit) and a unique play identifier.

  4. At session end (user taps "end session" in the App) TrackMan will call the webhook with a session message.

  5. After session end, TrackMan will initiate the session upload process to the cloud with all measurements, metadata, and optional videos captured during the session. This data will then be available via the data distribution system (currently CSV files on an FTP server).

Each message is sent once and there is no retry logic or buffering if the internet is temporarily disconnected or the customer endpoint is unresponsive. The TrackMan Baseball app *will* provide the user with an informal warning message if the ball tracking event could not be sent; this will not block subsequent ball tracking events in the App.

 

SubscriptionValidation Message

This is called when a new webhook is set up and *may* be called at certain intervals during the lifetime of the webhook. The webhook must extract the value of the element "validationCode" and generate an HTTP JSON response with a single element named "validationResponse" and a value equal to the validationCode received. See this example from Microsoft on how to do this in C#: https://github.com/Azure-Samples/event-grid-dotnet-publish-consume-events/blob/master/EventGridConsumer/EventGridConsumer/Function1.cs

[{
  "id": "4050306c-c472-4091-b0dc-5acba2c54ebb",
  "topic": "/subscriptions/something-redacted/resourceGroups/tmbaseball-contextualdata-prod/providers/microsoft.eventgrid/topics/webhookvalidationstopic",
  "subject": "",
  "data": {
    "validationCode": "E74D3BFA-63F5-48A7-8887-D9CC6EDFC69D",
    "validationUrl": "https://eventgrid.azure.net:553/eventsubscriptions/something-redacted"
  },
  "eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
  "eventTime": "2021-03-17T12:50:47.2830707Z",
  "metadataVersion": "1",
  "dataVersion": "2"
}]

 

 

Session Message

The payload of the webhook message is as follows:

{
"Version" : "3.0.0",
"Time": "2018-02-22T20:39:17.312076160Z",
"SessionId" : "abc93729-ac13-11e9-9ed5-989096a0d95d",
"SessionType" : "Pitching",
"Location" : {
"Venue" : {
"Name" : "Practice Field Trip 1"
},
"Field" : {
"Name" : "Lane 1"
}
},
"Batters" : [
{
"ForeignId" : "123456",
"NameRef" : "Zastryzny, Rob"
},
{
"ForeignId" : "345678",
"NameRef" : "Motter, Taylor"
}
],
"Pitchers" : [
{
"ForeignId" : "123456",
"NameRef" : "Henderson, Jason"
},
{
"ForeignId" : "345678",
"NameRef" : "Gonzarles, Juan"
}
],
"SessionState" : {
"State" : "SessionEnded",
"SessionStartedUtc" : "2019-07-22T06:49:55.5143686Z",
"SessionStartedLocal" : "2019-07-22T06:49:55.5143686",
"SessionEndedUtc" : "2019-07-22T09:49:55.5143686Z",
"SessionEndedLocal" : "2019-07-22T09:49:55.5143686"
}
}

 

Ball Message

The payload of the webhook message is as follows with the Pitch information coming first and the Hit information towards the bottom:

{
"Version": "1.2.0",
"Time": "2018-02-22T20:39:17.312076160Z",
"SessionId": "abc93729-ac13-11e9-9ed5-989096a0d95d",
"PlayId": "53427fe0-52e3-4017-9aa2-236c87718f6c",
"TrackId": "b2cc3109-7410-4150-867a-7b1bfb0fbc0c",
"TrackStartTime": "2019-09-04T14:37:14.531193Z",
"Kind": "Pitch",
"Pitch": {
"Release": {
"Speed": 0.0,
"SpinRate": null,
"Extension": 0.0,
"VerticalAngle": 0.0,
"HorizontalAngle": 0.0,
"Height": 1.812,
"SpinAxis3dTilt": "1:15",
"SpinAxis3dActiveSpinRate": 2126.816089,
"SpinAxis3dSpinEfficiency": 0.9985436257,
"SpinAxis3dTransverseAngle": 214.5010932,
"SpinAxis3dLongitudinalAngle": 3.092622133
},
"Location": {
"Time": 0.0,
"Height": 1.812,
"Side": 0.652
},
"Movement": {
"Horizontal": 0.0,
"Vertical": 0.0,
"InducedVertical": 0.0,
"SpinAxis": 247.914355
},
"NineP": {
"X0": [0.759779, 15.24, 1.680944],
"V0": [-2.688154, -41.431425, -0.591442],
"A0": [4.581591, 8.427442, -5.936245],
"Pfxx": 0.202506,
"Pfxz": 0.171071
}
},
"Hit": {
"Launch": {
"Speed": 0.0,
"SpinRate": null,
"VerticalAngle": 30.939,
"HorizontalAngle": 21.68
},
"LandingFlat": {
"Time": 4.30478,
"Distance": 100.771,
"Bearing": 30.179
}
}
}

 

  • Time" is UTC timestamp from when the message was last updated
  • "SessionId" is a unique session identifier from sessionStart
  • "Kind" can be Pitch or Hit
    • "Pitch" will only be with data if "Kind" = Pitch
    • "Hit" will only be with data if "Kind" = Hit

 

Was this article helpful?

0 out of 0 found this helpful

Have more questions? Submit a request