├── SUMMARY.md ├── README.md └── version ├── 1.0.0-alpha.2.md ├── 1.0.0-alpha.4.md └── 1.0.0-alpha.3.md /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [About fitness·json](README.md) 4 | 5 | ## Versions 6 | 7 | * [1.0.0-alpha.4](version/1.0.0-alpha.4.md) 8 | * [1.0.0-alpha.3](version/1.0.0-alpha.3.md) 9 | * [1.0.0-alpha.2](version/1.0.0-alpha.2.md) 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Version 1.0.0-alpha.2 3 | version: 1.0.0-alpha.2 4 | toc_footers: 5 | - 'Released: May 4, 2019' 6 | includes: 7 | - version-key 8 | - custom-extensions 9 | - all-versions 10 | - license 11 | search: true 12 | --- 13 | 14 | # About fitness·json 15 | 16 | **fitness·json** is a family of data interchange objects based on JavaScript Object Notation (JSON), designed specifically for storing and sharing sport, fitness, and health data. It defines several types of JSON objects and how they are combined to represent data about athletes' workouts, fitness profiles, and guided workout instructions. 17 | 18 | The whole ecosystem is designed for endurance athletes such as runners, cyclists, triathletes, para-triathletes, and the like. The format of the data is friendly to both computers and humans, and can be easily explored, adjusted, and extended. 19 | 20 | {% hint style="info" %} 21 | The latest version of fitness·json is [1.0.0-alpha.4](version/1.0.0-alpha.4.md). 22 | {% endhint %} 23 | 24 | ## Goals 25 | 26 | Create a simple, modern, human- and computer-friendly data interchange format for endurance sports. 27 | 28 | ## FAQ 29 | 30 | ### **Why another file format?** 31 | 32 | None of the existing file formats are both computer- *and* user-friendly. Most are XML schema extensions built on top of mapping or GIS file formats. The goal of **fitness·json** is to be simple, friendly, and easily parsable by both modern apps and human eyes. 33 | 34 | ### **Can I use fitness·json in my application/project?** 35 | 36 | Yes. The file format is open and intended for wide adoption. 37 | 38 | ### **Can I contribute to fitness·json?** 39 | 40 | Yes, you can use custom extensions to store any information in fitness·json files without approval from the community. Alternatively, you can suggest features and changes for the next fitness·json release in our [community forums](https://tride.community/fitnessjson). 41 | 42 | ### **Is there a license fee?** 43 | 44 | No, fitness·json is entirely open and free to use and adapt. Everything about the format is as open-source as possible. The evolution process is also happening in the open. 45 | -------------------------------------------------------------------------------- /version/1.0.0-alpha.2.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Complete specification and documentation of fitness·json file format family. 3 | --- 4 | 5 | # 1.0.0-alpha.2 6 | 7 | fitness·json is a family of data interchange objects based on JavaScript Object Notation \(JSON\) designed specifically for the storing and sharing of sport, fitness, and health data. It defines several types of JSON objects and how they are combined to represent data about athlete's workouts, fitness profile, and guided workout instructions. 8 | 9 | The whole ecosystem is designed for endurance athletes such as runners, cyclists, triathletes, para-triathletes, and alike. Format of the data is friendly to both—computers and humans—and can be easily explored, adjusted, and extended. 10 | 11 | {% hint style="danger" %} 12 | This is an alpha version of the fitness·json. Things will break, frequently. Spec is not ready for the prime time, or usage in production software. 13 | {% endhint %} 14 | 15 | ### Requirements Language 16 | 17 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119). 18 | 19 | ### File Format 20 | 21 | fitness·json objects can be stored to persistent storage as files. Such, self-contained objects must have `version` key at the very top of the root object. 22 | 23 | Files should be pretty-printed as it is friendlier to humans. In cases where larger file sizes are an issue, the content may be compacted by stripping the whitespaces. 24 | 25 | * File extension\(s\): `.json`, `.fitjson` 26 | * Macintosh UTI: `public.fitjson` conforms to `public.json` 27 | 28 | ## Workout 29 | 30 | Workout object is self-contained and can be represented as a single file. Purpose of the workout object is to contain all activities completed as one workout along with its completion status, post-workout comments, and athlete's profile at the moment of workout. 31 | 32 | ### Top Level 33 | 34 | > Example of the completed workout: 35 | 36 | ```javascript 37 | { 38 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#workout", 39 | "title": "2019-04-29 [T1] Tempo Run (interupted)", 40 | "status": "complete", 41 | "activities": [ 42 | { 43 | "sport": "cycle", 44 | "env": "outdoor", 45 | "start_date": "date", 46 | "samples": [ 47 | { 48 | "t": 123123123, 49 | "hr": 84, 50 | "p": 189 51 | }, 52 | { 53 | "t": 123123123, 54 | "hr": 85, 55 | "p": 174 56 | }, 57 | { 58 | "t": 123123123, 59 | "hr": 83, 60 | "p": 182 61 | } 62 | ], 63 | "laps": [ 64 | { "t": 1235323, "trigger": "user" } 65 | ] 66 | }, 67 | { 68 | "sport": "run", 69 | "env": "outdoor", 70 | "start_date": "date", 71 | "samples": [ 72 | { 73 | "t": 123123123, 74 | "hr": 110, 75 | } 76 | ] 77 | } 78 | ] 79 | } 80 | ``` 81 | 82 | Typical workout object contains an array of [activities](../#activity), valid [status](../#activity-options) value, and [version](../#top-level). 83 | 84 | Note that workouts start date is `start_date` of its first activity. Workouts with empty `activities` array and status anything other than `empty` are invalid. 85 | 86 | | Key | Type | Req. | Description | 87 | | :--- | :--- | :---: | :--- | 88 | | `version` | string | ✓ | URL of the version of the format the object and/or file. | 89 | | `title` | string | | Title of the workout for easy identification. | 90 | | `activities` | [\[activity\]](../#activity) | ✓ | An array of activities that are part of the workout. If the array has more that one activity, the workout is considered to be a _multi-sport workout_.\* | 91 | | `status` | [status](../#activity-options) | ✓ | Completion status of the workout. | 92 | | `comments` | [\[comment\]](../#comment) | | Post-workout comments or discussion. May contain many [comments](../#comment) from various [people](../#author). | 93 | | `profile` | [profile](../#profile) | | A snapshot of the profile at the time of workout completion.[‡]() | 94 | | `guide` | [guide](../#guide) | | A snaptshot of the performed guide. Optional; athelete may not want to share this. | 95 | | `route` | [route](../#route) | | A snaptshot of the route if athelete was suing one uppon completion of the workout. Optional; athelete may not want to share this. | 96 | 97 | #### Triathlon and Multisport Transitions 98 | 99 | Triathlon and multisport transitions are distinct activities. Such objects have `sport: transition` key-value pair in them. 100 | 101 | ### Workout Status 102 | 103 | Workouts may have one of several statuses depending on circumstances when workout object was stored. 104 | 105 | | Value | Description | 106 | | :--- | :--- | 107 | | `empty` | The workout is newly created or has no meaningful data. | 108 | | `incomplete` | Workout has incomplete data. An athlete should have an opportunity to carry-on recording data. | 109 | | `complete` | **Default** value. The workout is complete and must not be modified. | 110 | 111 | #### Causes of Incomplete Workout 112 | 113 | Incomplete workouts can exist for several reasons. One of those could be a recorder malfunction. Consider the following when you face such condition: 114 | 115 | 1. Recorder should write workout object to persistent storage periodically so that in case of the fatal crash only a minimal amount of data is lost. 116 | 2. The recorder should set the `status` key to `incomplete` while recording and resets it to `complete` only when an athlete explicitly finishes the workout \(or, guided workout finishes the activity.\) 117 | 3. When `status` is `incomplete`, the athlete should have an opportunity to restore the recording state and carry-on activity within a reasonable time interval after the last sample. 118 | 119 | ### Embeded Profile 120 | 121 | > Example of a workout with an embedded profile: 122 | 123 | ```text 124 | { 125 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#workout", 126 | "activities": [{ }], // redacted 127 | "status": "complete", 128 | "profile": { 129 | "lthr": 169, 130 | "ftp": 220, 131 | "vo2max": 39, 132 | "weight": 74.4, 133 | } 134 | } 135 | ``` 136 | 137 | Athlete's fitness level and body measurements change over time, hence the variable nature of [profile]() over time. To provide better context for the workout, athletes may include their profile in completed workout object for later reference. 138 | 139 | For instance, let's imagine an athlete preparing for an upcoming cycling event. Her weight three weeks ago was different from the current. Because of that, when she's analyzing data from three weeks old workout, her `w/kg` power measurements must be calculated from the embedded profile that possesses her weight at the time of the workout. This guarantees measurements she's looking at match reality at the time of the workout. 140 | 141 | ### Sharing Practices 142 | 143 | Information stored in workout objects and files can be highly sensitive, both from the privacy perspective, as well as the athlete's competitive advantage. 144 | 145 | When sharing workout online or with another party, consider the following: 146 | 147 | * The sharer should not share object as-is unless athlete explicitly consents such action. 148 | * The app should provide an opportunity to omit information from the object before sharing it online. Good candidates for removal are the complete profile, cycling power from every sample, comments, partial profile \(e.g., include `weight`, but omit `email` and `ftp`\). 149 | 150 | ## Activity 151 | 152 | ```javascript 153 | { 154 | "sport": "cycle", 155 | "env": "outdoor", 156 | "start_date": "date", 157 | "samples": [ ], 158 | "laps": [ ], 159 | "markers": [ ] 160 | } 161 | ``` 162 | 163 | Activity object describes one activity of particular sport performed in a single environment. Some examples of activity are _Outdoor Run_, _Indoor Cycle_, _Outdoor Handcycle_, _Open-water Swim_, and _Outdoor \[Triathlon\] Transition_. An ordered sequence of activities put in workout object forms a multi-sport workout. Most workouts contain single activity. 164 | 165 | Apart from describing the environment and sport, activity objects contain recorded data. Data collected from various sensors and data providers must be sequentially stored in [samples]() array. 166 | 167 | Additionally, activities contain [lap events]() and [markers](). 168 | 169 | #### Top Level 170 | 171 | The typical activity contains [sport](), [environment](), start date, and an array of [samples](). 172 | 173 | | Key | Type | Req. | Description | 174 | | :--- | :--- | :---: | :--- | 175 | | `version` | string | ✓ | URL of the version of the format the object. | 176 | | `sport` | [sport]() | ✓ | The sport of the activity. | 177 | | `env` | [environment]() | ✓ | An environment of the activity. | 178 | | `start_date` | [date]() | ✓ | Date when activity is started. This date may not match `t` value from the first sample, as data from the senrsos may be delayed. | 179 | | `samples` | [\[sample\]]() | ✓ | Samples recorded during the activity ordered chronologically. Oldest samples first. | 180 | | `laps` | [\[lap\]]() | | An array of all lap events. Both, automatic and user-generated. | 181 | | `markers` | [\[marker\]]() | | An array of user markers. | 182 | | `options` | [\[string\]]() | | Set of accessibility options. | 183 | 184 | #### Activity Options 185 | 186 | > Example of activity performed on a tandem bicycle: 187 | 188 | ```text 189 | { 190 | "sport": "cycle", 191 | "env": "outdoor", 192 | "options": ["PTVIx"], 193 | "start_date": "2019-04-20T04:20:42+00:00", 194 | "samples": [ 195 | // { ... } 196 | ], 197 | } 198 | ``` 199 | 200 | Some activities can not be described completly using evironment and sport alone. For such cases, `options` should provide more information. 201 | 202 | | Value | Description | 203 | | :--- | :--- | 204 | | `PTWCx` | Athlete uses a recumbent handcycle on the bike course and a racing wheelchair on the run segment \(in context of triathlon.\) | 205 | | `PTSx` | In both bike and run segments \(in context of triathlon,\) amputee athletes use prosthesis or other supportive devices. | 206 | | `PTVIx` | Athelete rides a tandem. | 207 | 208 | Above values subject to major redesign. 209 | 210 | ### Sample 211 | 212 | The sequence of ordered samples is the foundation of successful data analysis. 213 | 214 | > Annotated example of a sample: 215 | 216 | ```text 217 | { 218 | "t": 1555678832, // datetime, unix epoch time 219 | "hr": 80, // heart rate, bpm 220 | "s": 1.4, // speed, m/s 221 | "l": { // location object 222 | "lt": 36.486331, // latitude 223 | "ln": 136.756827, // longitude 224 | "ha": 5 // horizontal accuracy, meters 225 | }, 226 | "p": 200, // power, watts 227 | "pb": 0.54, // power left-right ratio 228 | "el": 0.2, // elevation delta, meters 229 | "alt": 2314.2, // altitude, meters 230 | "grd": 2.1, // grade, % slope 231 | "at": 24.7, // temperature, celsius 232 | "d": 3.12, // distance, meters 233 | "c": 80, // cadecne, rpm 234 | "crs": 98.34 // course, degrees 235 | } 236 | ``` 237 | 238 | | Key | Type | Unit | Description | 239 | | :--- | :--- | :---: | :--- | 240 | | `t` | [datetime]() | | MThe moment when sensors and data providers were sampled for the measurements. | 241 | | `hr` | int | BPM | Heart rate measurement | 242 | | `l` | [location]() | | Geographical 2D location of an athlete | 243 | | `p` | double | W | Total power measurement | 244 | | `pb` | double | | Power bias, or distributions between left and right limbs. Must be ignored if the sample has no `p` measurement. | 245 | | `c` | int | rpm[†]() | Cadence measurement | 246 | | `s` | double | m/s | Speed measurement | 247 | | `crs` | double | ºN | Course—the direction in which the athlete is traveling, measured in degrees and relative to due north. | 248 | | `d` | double | m | Displacement—the distance from the last sample. | 249 | | `alt` | double | m | Absolute altitude of an athlete from sea level. | 250 | | `el` | double | m | Elevation—the altitude delta from the beginning of the activity. | 251 | | `vosc` | double | cm | Vertical oscillation measurement \(part of running dynamics.\) | 252 | | `gct` | double | ms | Ground contact time measurement—the time an athlete spends touching ground during the running stride \(part of running dynamics.\) | 253 | | `stpl` | double | m | Step length—the length of the single stride \(part of running dynamics.\) | 254 | | `grd` | double | % | Grade of the slope athlete is on. | 255 | | `at` | double | ºC | Ambient temperature measurement. | 256 | 257 | An empty sample is considered to be invalid and must be ignored. Non-empty sample must contain **`t` and at least one other measurement**. Sample containing the only `t` is also invalid and must be ignored. 258 | 259 | #### Location 260 | 261 | > Example of a location object with a latitude, longitude, horizontal accuracy of 30 centimeters and vertical accuracy of 5 meters: 262 | 263 | ```javascript 264 | { 265 | "lt": 35.679687, 266 | "ln": 139.757583, 267 | "ha": 0.3, 268 | "va": 5, 269 | } 270 | ``` 271 | 272 | Location object describes a geographical location on a 2D plane. Location object may include 2D or 3D precision of the location. Precision values may help data analysis software to calculate an athlete's relative path in a more precise manner. 273 | 274 | | Key | Type | Req. | Unit | Description | 275 | | :--- | :--- | :---: | :---: | :--- | 276 | | `lt` | double | ✓ | degrees[†]() | The latitude in degrees | 277 | | `ln` | double | ✓ | degrees[†]() | The longitude in degrees | 278 | | `ha` | double | | m | Horizontal accuracy | 279 | | `va` | double | | m | Vertical accuracy | 280 | 281 | It is recommended to store unprocessed location data from the sensors. Smoothing and other filtering techniques can be applied after data is decoded. 282 | 283 | #### Latitude and Longitude Units 284 | 285 | Positive values indicate latitudes north of the equator. Negative values indicate latitudes south of the equator. 286 | 287 | #### Cadence Units 288 | 289 | Cadence unit depends on the [sport](). For simplicity's sake, it is generalized as RPM \(revolutions per minute\). However, it could mean SPM \(steps per minute\) for running, and PPM \(pushes per minute\) for a wheelchair. 290 | 291 | #### Power Bias 292 | 293 | Power bias \(value of `pb`\), or power distribution is a ratio of work between athlete's left and right limbs. Value of `0.0` means that the athlete's right limb is doing all of the work and value of `1.0` means that the athlete's left limb is doing all of the work. 294 | 295 | ```javascript 296 | { 297 | "t": 1555750866, 298 | "p": 234, 299 | "pb": 0.53 300 | } 301 | ``` 302 | 303 | For instance, imagine athlete producing 234 watts on a bicycle equipped with a dual-side power meter. Power bias value is 0.53, meaning athlete is producing 124w \(53% of the total power\) with the left leg and 110w \(47%\) with the right leg. 304 | 305 | A sample may not have power bias object while having value for `p`. Such a case, as an example, maybe be when the bicycle is equipped with a left-only power meter. 306 | 307 | ### Lap 308 | 309 | > Typical lap event triggered by an athlete: 310 | 311 | ```javascript 312 | { 313 | "t": "2019-04-20T04:20:42+00:00", 314 | "trigger": "manual" 315 | } 316 | ``` 317 | 318 | Lap object describes an event. Lap events can be triggered manually by an athlete, or automatically—by fitness equipment or the [guide](). 319 | 320 | | Key | Type | Req. | Description | 321 | | :--- | :--- | :---: | :--- | 322 | | `t` | datetime | ✓ | Datetime of the event triggered | 323 | | `trigger` | [lap\_trigger]() | | DSescribes what or who triggred an event. | 324 | 325 | #### Lap Trigger 326 | 327 | Lap trigger describes means by which lap event was initiated. 328 | 329 | | Value | Description | 330 | | :--- | :--- | 331 | | `unknown` | **Default** value. Asumed when no `trigger` is present in lap object. | 332 | | `manual` | Athelete pushed _Lap_ button. | 333 | | `distance` | Distance based auto-lap feature of an app. | 334 | | `timer` | Time based auto-lap feature of an app. | 335 | | `posision` | Geographical position or fence. | 336 | | `equipment` | Fitness equpement requeested start of new lap. | 337 | | `guide` | [Guide]() procedded to next interval or block. | 338 | 339 | ### Marker 340 | 341 | > Simple marker noting a particular moment: 342 | 343 | ```javascript 344 | { 345 | "t":"2019-04-20T04:20:42+00:00" 346 | } 347 | ``` 348 | 349 | > Marker with a note: 350 | 351 | ```javascript 352 | { 353 | "t":"2019-04-20T04:20:42+00:00", 354 | "note": "Feels like I've hit the wall." 355 | } 356 | ``` 357 | 358 | Marker object describes a specific point in time of the activity for the later reference. Marker object `t` value can be mapped to the corresponding sample and its measurements by the data analysis software. 359 | 360 | | Key | Type | Req. | Description | 361 | | :--- | :--- | :---: | :--- | 362 | | `t` | datetime | ✓ | Mokent of time to mark. | 363 | | `trigger` | string | | Short note. | 364 | 365 | ## Guide 366 | 367 | Guide, or Guided Workout, is an object or file with a set of instructions of how the athlete should perform the activity. Guides are great tools for structured workouts as they can be as loose or as strict as the author designs them to be. 368 | 369 | ### Anatomy of Guide 370 | 371 | There are couple of object object that make up a single guide. 372 | 373 | 👉 TODO: Visualisation of the guide, its intervals, and blocks. 374 | 375 | Guides consist of [intervals](), [blocks]() are groups of blocks, and blocks are atomic objects that describe a single step of work in the guide. The guide can be visualized as a queue of blocks. Intervals unify some blocks into groups and tells the guide what environment, sport and repeat count they share. 376 | 377 | #### Top Level 378 | 379 | ```text 380 | { 381 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#gudie", 382 | "title": "Tempo Ride", 383 | "symbol": "T1", 384 | "symbol_color": "#FF9900", 385 | "intervals": [ 386 | { 387 | // ... 388 | } 389 | ], 390 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 391 | "author": { 392 | "name": "John Appleseed", 393 | "email": "johnappleseed@example.com", 394 | "link": "https://leopardskins-cyclingclub.org" 395 | }, 396 | "created_at": "2019-04-21T00:41:57+00:00", 397 | "ref": "https://leopardskins-cyclingclub.org/guides/" 398 | } 399 | ``` 400 | 401 | Non-empty guides must have at least one interval with at least one block. Otherwise, the guide is considered to be empty. The empty guide is not suitable for workout and should be ignored or edited. 402 | 403 | | Key | Type | Req. | Description | 404 | | :--- | :--- | :---: | :--- | 405 | | `version` | string | ✓ | URL of the version of the format the object and/or file. | 406 | | `title` | string | | Title of the guide. | 407 | | `symbol` | string | | Abbreviation or shorthand code for easy guide identification. No more than two characters. | 408 | | `symbol_color` | string | | Color of the symbol for easy guide identification. A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 409 | | `description` | string | | A relatively long-form description of the guide. An excellent place to inform the athlete what this guide is trying to achieve, what to expect during the workout and any other information that may be useful for the athlete before she starts the workout. | 410 | | `intervals` | [\[interval\]]() | ✓ | An array of ordered intervals. | 411 | | `author` | [author]() | | Name and contact information of the author of this guide. | 412 | | `created_at` | datetime | ✓ | The date and time of guide creation. | 413 | | `ref` | string | | Reference URL pointing to any auxiliary or related information. | 414 | 415 | ### Interval 416 | 417 | > Example of segnment with two blocks that will be repeated twice in the workout. 418 | 419 | ```text 420 | { 421 | "title": "Max Power", 422 | "env": "outdoor", 423 | "sport": "run", 424 | "repeats": 2, 425 | "blocks": [ 426 | { 427 | // ... 428 | }, 429 | { 430 | // ... 431 | } 432 | ], 433 | "description": "Maximum effort followed by 2 min rest. Two sets. Go!" 434 | } 435 | ``` 436 | 437 | Interval objects provide structure to the guide. They group related blocks, as well as tell the guide what environment and sport those blocks should be performed. Intervals also can have repeat count, thus eliminating the need to have duplicate groups of blocks in sequence. 438 | 439 | | Key | Type | Req. | Description | 440 | | :--- | :--- | :---: | :--- | 441 | | `repeats` | int \[1..∞\)[†]() | | The number of repeats. Describes how many times a set of blocks should be repeated during a workout. | 442 | | `title` | string | | Title of the interval. | 443 | | `sport` | [sport]() | ✓ | Sport. | 444 | | `env` | [environment]() | ✓ | Environment. | 445 | | `blocks` | [\[block\]]() | ✓ | An array of ordered blocks that are part of this interval. | 446 | | `description` | string | | Short instruction and overview of the block for the athlete. | 447 | 448 | #### Repeats 449 | 450 | When `repeat` has a value greater than `1`, the guide should put the interval's first block in the queue right after its last block. Only after blocks in the interval has been performed `repeat` times, the guide must advance to the next interval \(first block of the next interval\). This process should repeat until the guide has run out of blocks and their parent intervals. 451 | 452 | `repeat` is optional, and when absent from the object, the decoder should assume the value of `1`. Values less than `1` are invalid. 453 | 454 | ### Block 455 | 456 | ```javascript 457 | { 458 | "type" : "work", 459 | "duration" : { 460 | "condition" : "gte", 461 | "trigger" : "distance", 462 | "value" : 5000 463 | }, 464 | "target_type" : "rvalue", 465 | "target_rvalue" : { 466 | "anchor" : "threshold", 467 | "lower" : 0.95, 468 | "upper" : 1.05, 469 | "metric" : "heart_rate" 470 | }, 471 | "description" : "Run for 5K. Try to be near your LTHR.", 472 | } 473 | ``` 474 | 475 | Block object, or work block, describes a single unit of work in the guide. Block can be one of [several types](), have completion condition, and optionally, target. Targets itself, can be [one of four types](). 476 | 477 | | Key | Type | Req. | Description | 478 | | :--- | :--- | :---: | :--- | 479 | | `type` | [type]() | ✓ | Type of the block. Describes the kind of work to perform. | 480 | | `duration` | [duration]() | ✓ | The condition upon meeting which block is complete and guide advances to the next block. | 481 | | `target_type` | [target\_type]() | ✓ | Type of the target. | 482 | | `target_value` | [value\_target]() | | The object describing target when `target_type` is `value`. | 483 | | `target_rvalue` | [rvalue\_target]() | | The object describing target when `target_type` is `rvalue`. | 484 | | `target_zone` | [zone\_target]() | | The object describing target when `target_type` is `zone`. | 485 | | `description` | string | | Short instruction and overview of the block. | 486 | 487 | #### Block Type 488 | 489 | Block type object describes the nature of work in the current block. Those are provided mostly for visual reference. Type of work does not affect the block's completion duration, nor on target. 490 | 491 | | Value | Description | 492 | | :--- | :--- | 493 | | `warmup` | Warm-up. | 494 | | `work` | Relatively hi-intensity activity. | 495 | | `rest` | Lower intensity activity. | 496 | | `cooldown` | Cooldown. | 497 | 498 | ### Block Completion 499 | 500 | The guide can advance by itself or manually. Block completion object describes exact instruction on how the block should advance to the next block in the guide. 501 | 502 | | Key | Type | Req. | Description | 503 | | :--- | :--- | :---: | :--- | 504 | | `trigger` | [trigger]() | ✓ | What triggers advancement to the next block. | 505 | | `condition` | [condition]() | | What conditions should be satisfied by the trigger. | 506 | | `value` | double | | Value of measurement based on which condition is applied. | 507 | 508 | #### Trigger 509 | 510 | > Example of a cooldown block that ends when the athlete's heart rate drops below 60 BPM: 511 | 512 | ```javascript 513 | { 514 | "duration": { 515 | "condition": "lt", 516 | "trigger": "heart_rate", 517 | "value": 60 518 | }, 519 | "description": "Run for 5K", 520 | "type": "cooldown", 521 | "target_type": "none" 522 | } 523 | ``` 524 | 525 | Event or measurement that triggers the advancement to the next block or guide completion. When the trigger is set to `user`, the guide will not advance automatically. It is good practice to remind athletes about that using the block's description object. Athlete can manually advance to the next block irrespective what trigger value is. 526 | 527 | | Value | Description | 528 | | :--- | :--- | 529 | | `user` | Athlete requests next block. | 530 | | `time` | Block timer. | 531 | | `distance` | Block distance. | 532 | | `hr` | Live heart rate. | 533 | | `block_hr` | Block avarage heart rate. | 534 | | `speed` | Live speed or pace. | 535 | | `block_speed` | Block avarage speed or pace. | 536 | | `cadence` | Live cadence. | 537 | | `power` | Live power. | 538 | | `power3` | Live power avarege of last 3 seconds. | 539 | | `power10` | Live power avarege of last 10 seconds. | 540 | | `power30` | Live power avarege of last 30 seconds. | 541 | | `block_power` | Block avarage power. | 542 | | `swim_stroke` | Swim stroke count. | 543 | 544 | #### Condition 545 | 546 | > Example of the block that advances to the next block after 5K: 547 | 548 | ```text 549 | { 550 | "duration": { 551 | "condition": "gte", // note `gte` instead of `eq` 552 | "trigger": "distance", 553 | "value": 5000 554 | }, 555 | "description": "Run for 5K", 556 | "type": "work", 557 | "target_type": "none" 558 | } 559 | ``` 560 | 561 | > You could read out loud above block's completion condition like so: _“This block will be done when athlete covers the distance that is greater than or equal to 5000 meters.”_ 562 | 563 | Condition object describes an operator that is applied to the measurement and value of value object in the block completion. 564 | 565 | | Value | Symb. | Description | 566 | | :--- | :---: | :--- | 567 | | `lt` | < | Less than | 568 | | `lte` | ≤ | Less than or equal to. | 569 | | `eq` | = | Strictly equal to. | 570 | | `gte` | ≥ | Greater than or equal to. | 571 | | `gt` | > | Greater than. | 572 | 573 | Note that equality \(`eq`\) is strict equality. When designing blocks that are complete at after 5000 meters, guide designer must use greater than or equal to condition operator instead of equal to as recorder might sample distance values just before and after athlete passes 5K mark. Neither 4998.9 not 5000.4 are strictly equal to 5000, thus block never ends. 574 | 575 | ### Block Targets 576 | 577 | > Example block without a target has a duration of two minutes: 578 | 579 | ```javascript 580 | { 581 | "type": "work", 582 | "target_type": "none", 583 | "duration": { 584 | "condition": "gte", 585 | "trigger": "time", 586 | "value": 120 587 | }, 588 | "description": "Simply run for two minutes.", 589 | } 590 | ``` 591 | 592 | Block object can have one of four types of targets as described in `target_type`. 593 | 594 | | Value | object Key | Description | 595 | | :--- | :--- | :--- | 596 | | [`none`]() | N/A | Block has no target \(a.k.a. _Free Block_.\) | 597 | | [`value`]() | `target_value` | The athlete is asked to be in the range of specific numbers for the given [metric](). | 598 | | [`rvalue`]() | `target_rvalue` | The athlete is asked to be the range that is calculated by multiplying relative values to fitness values from the [profile]() of the given [metric]() and [anchor](). | 599 | | [`zone`]() | `target_zone` | The athlete is asked to hit zone at the given index. Zone index is independent of the athlete's choice zone set, The guide should specify what zone-set it is designed to work. | 600 | 601 | Unless `none` is a value of `target_type`, the block must have a corresponding target object as its content. 602 | 603 | #### No Target \(Free Block\) 604 | 605 | No target block. The athlete may do whatever they want. This kind of block is useful when "target" is to complete the block. 606 | 607 | #### Absolute Value Target 608 | 609 | > Example of block with `target_type: value`. An athlete is asked to maintain heart rate within 80–100 BPM range for 20 minutes: 610 | 611 | ```javascript 612 | { 613 | "type": "work", 614 | "target_type": "value", 615 | "target_value": { 616 | "metric": "heart_rate", 617 | "lower": 80, 618 | "upper": 100, 619 | }, 620 | "duration": { "condition": "gte", "trigger": "time", "value": 1200 } 621 | } 622 | ``` 623 | 624 | Absolute value target object describes the specific range of values of a given [metric]() that athlete should aim. 625 | 626 | All fields are required. 627 | 628 | | Key | Type | Description | 629 | | :--- | :--- | :--- | 630 | | `metric` | [metric]() | Target metric | 631 | | `lower` | double | Lower bound | 632 | | `upper` | double | Upper bound \(exclusive\) | 633 | 634 | While absolute value targets are straightforward and easy to understand, note that it is infrequent when the same values work for different athletes. If you intent sharing the guide online, consider using [Relative Value Target]() objects as they calculate absolute ranges based on an athlete's profile. 635 | 636 | #### Relative Value Target 637 | 638 | > Example of the block with `target_type: rvalue`. An athlete is asked to maintain 90–110% of her FTP during 2 minutes: 639 | 640 | ```javascript 641 | { 642 | "type": "work", 643 | "target_type": "rvalue", 644 | "target_value": { 645 | "metric": "power", 646 | "anchor": "threshold", 647 | "lower": 0.9, 648 | "upper": 1.1, 649 | }, 650 | "duration": { "condition": "gte", "trigger": "time", "value": 120 } 651 | } 652 | ``` 653 | 654 | Unlike absolute value target, relative values targets are calculated based on data points from the athlete's [profile](). They can be universal for many types among the people and automatically change over time as an athlete's physique and profile changes. As with absolute values, the target range is set with `lower` and `upper` bound values, but instead of exact measurements, they contain multipliers. Those values get multiplied by a measurement from profile based on the combination of values from `metric` and `anchor`. 655 | 656 | For instance, if the value of `metric` is `power` and value of `anchor` is `threshold`, then multipliers from the target's `lower` and `upper` are applied to the [profile]()'s `ftp` value. 657 | 658 | All fields are required. 659 | 660 | | Key | Type | Description | 661 | | :--- | :--- | :--- | 662 | | `metric` | [metric]() | Target metric | 663 | | `anchor` | [anchor]() | Relative to what profile measurement | 664 | | `lower` | double | Lower bound multiplier | 665 | | `upper` | double | Upper bound multiplier \(exclusive\) | 666 | 667 | #### Zone Target 668 | 669 | > Example of the block with `target_type: zone`. An athlete is asked to maintain heart rate zone 3 for 4 kilometers: 670 | 671 | ```javascript 672 | { 673 | "type": "work", 674 | "target_type": "zone", 675 | "target_value": { 676 | "metric": "heart_rate", 677 | "index": 3, 678 | }, 679 | "duration": { "condition": "gte", "trigger": "distance", "value": 4000 } 680 | } 681 | ``` 682 | 683 | Zone Target object describes which zone athlete should aim for, and what kind of zone it should be. Note that the value of `index` is an index of a zone in an array of zones in a zone set. Meaning, if an athlete replaces zone set with a different one, a new zone that is located at the same index of `index` becomes the new target zone. It is an athlete's responsibility to make sure that selected zone set matches requirements of the target and guide. 684 | 685 | All fields are required. 686 | 687 | | Key | Type | Description | 688 | | :--- | :--- | :--- | 689 | | `metric` | [metric]() | Type of metric of which zone should be used. | 690 | | `index` | int | Index of a selected zone-set. | 691 | 692 | #### Metric 693 | 694 | Metric object describes what type of measurement target value range is. 695 | 696 | | Value | Description | 697 | | :--- | :--- | 698 | | `heart_rate` | Heart rate | 699 | | `cadence` | Cadence — RPM for cycling and handcycling; SPM for running; PPM for wheelchair. | 700 | | `power` | Power. | 701 | | `speed` | Speed or pace. | 702 | 703 | #### Metric Anchor 704 | 705 | Anchor object describes relative to which data point in athlete's [profile]() the final target range should be calculated against. 706 | 707 | | Value | Description | 708 | | :--- | :--- | 709 | | `min` | An athlete's minumum measured value for the metric \(e.g. **Resting HR** when `metric: heart_rate`.\) | 710 | | `max` | An athlete's maximum measured value for the metric \(e.g. **MaxHR** when `metric: heart_rate`.\) | 711 | | `threshold` | An athlete's threashold value for the metric \(e.g. **FTP** when `metric: power` and `sport: cycle`.\) | 712 | | `test` | Test time measurement \(e.g. swim test time\) | 713 | 714 | There are many combinations of `metric` and `anchor` that do not make sense. Such cases should be handled with care. For instance, what is minimum power? Minimum power athlete can produce is always equal to 0 watts \(i.e., not pedaling.\) Anchoring a target range on 0 watts is not particularly useful since both, lower and upper bounds always equal to 0. 715 | 716 | ## Profile 717 | 718 | > Example of an athelete's complete profile: 719 | 720 | ```javascript 721 | { 722 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#profile", 723 | "name": "John Appleseed", 724 | "email": "johnappleseed@example.com", 725 | "hrr": 48, 726 | "hrmax": 201, 727 | "lthr": 169, 728 | "ftp": 220, 729 | "rftpw": 240, 730 | "vo2max": 39, 731 | "weight": 74.4, 732 | "height": 170, 733 | "swim_test_dist": 1000, 734 | "swim_test_time": 500, 735 | "run_tpace": 4.7, 736 | "birthday": "1988-04-17", 737 | "sex": "male", 738 | "wheelchair": false, 739 | "handcycle": false 740 | } 741 | ``` 742 | 743 | > Example of the minimal profile for a cyclist: 744 | 745 | ```javascript 746 | { 747 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2.0#profile", 748 | "lthr": 169, 749 | "ftp": 220, 750 | "vo2max": 39, 751 | "weight": 74.4, 752 | } 753 | ``` 754 | 755 | Profile object describes various athlete's measurements and data points. Profile object features several arbitrary categories: basic body measurements, fitness test results, accessibility information, and contact information. 756 | 757 | Profile object is not a database; hence there is no historical information in it. When faced with a profile without any context, the decoder should assume that profile has _latest known data_. When profile object is part of another object \([for instance, a workout]()\), then the decoder can map the parent object's date to the profile. 758 | 759 | #### Top Level 760 | 761 | Since profile may be embedded into other objects, all values are optional. 762 | 763 | | Key | Type | Unit | Description | 764 | | :--- | :--- | :--- | :--- | 765 | | `version` | url | | URL of the version of the format the object. | 766 | | `name` | string | | Full name of an athlete. | 767 | | `email` | string | | Contact email of an athlete. | 768 | | `birthday` | [datetime]() | | Date of birth. | 769 | | `sex` | [sex]() | | Biological sex of an athelete. | 770 | | `hrr` | int | BPM | Resting heart rate. | 771 | | `hrmax` | int | BPM | Maximum heart rate. | 772 | | `lthr` | int | BPM | Lactate threashold heart rate, LTHR. | 773 | | `ftp` | int | W | Cycling threashold power, FTP. | 774 | | `rftpw` | int | W | Runnning functional threashold power, rFTPw. | 775 | | `vo2max` | int | mL/\(kg·min\) | Maximal oxygen consumption of an athlete. | 776 | | `weight` | int | kg | Weight of an athelete. | 777 | | `swim_test_dist` | double | m | Distance of the swim test. | 778 | | `swim_test_time` | double | s | Recorded completion time of the swim test \(for the distance.\) | 779 | | `run_tpace` | double | m/s | Running threashold pace. | 780 | | `wheelchair` | bool | | `true` when atehlete is a wheelchair user. Ommition of this key is equvalentn of `false` value. | 781 | | `handcycle` | bool | | `true` when atehlete is a handlcuyle user. Ommition of this key is equvalentn of `false` value. | 782 | 783 | `swim_test_dist` and `swim_test_time` are only interdependent values in the profile object. They are considered as valid only if both are present. 784 | 785 | Developers should pay extra attention when sharing profile to other parties or apps, as it may contain highly private information, both from user privacy, and competitive perspective. **Do not share a profile object in any form without user consent.** 786 | 787 | ## Route 788 | 789 | > Example of a route with single POI and two paths: 790 | 791 | ```javascript 792 | { 793 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#route", 794 | "name": "Sunday Group Ride (Spring 2019)", 795 | "description": "Note for new members: we'll take alternative path starting from 30km point (marked so) to avoid two climbs.", 796 | "paths": [ 797 | { 798 | "waypoints": [ 799 | { "lt": 36.486331, "ln": 136.756827 }, 800 | { "lt": 36.486331, "ln": 136.756827 }, 801 | { "lt": 36.486331, "ln": 136.756827 }, 802 | ], 803 | "name": "Main Cycling Path", 804 | "color": "#F7C948" 805 | }, 806 | { 807 | "waypoints": [ 808 | { "lt": 36.486331, "ln": 136.756827 }, 809 | { "lt": 36.486331, "ln": 136.756827 }, 810 | { "lt": 36.486331, "ln": 136.756827 }, 811 | ], 812 | "name": "Novice Detour", 813 | "color": "#8DED2D" 814 | } 815 | ], 816 | "poi": [ 817 | { 818 | "coordinate": { "lt": 36.486331, "ln": 136.756827 }, 819 | "name": "Coffee Stop", 820 | "color": "#E67635" 821 | } 822 | ], 823 | "ref": "https://leopardskins-cyclingclub.org/ride/1234" 824 | } 825 | ``` 826 | 827 | Routes are geographical information about paths and POIs \(points-of-interest\) used as a map overlay while performing an activity. Routes shouldn't require specific workouts but may have additional information. The primary use of the route is to serve as additional geographical information for an athlete regardless of activity or sport. Example paths could be weekly sunday bike ride route with its alternative loops, and resting and coffee-stop locations. 828 | 829 | The athlete may choose to use routes as an assistance to a guided workout and have POIs indicating starts or ends of significant efforts or target heart rate zones. 830 | 831 | #### Top Level 832 | 833 | Route files are self-contained and not part of any other file or object. 834 | 835 | | Key | Type | Req. | Description | 836 | | :--- | :--- | :---: | :--- | 837 | | `version` | url | ✓ | URL of the version of the format the file. | 838 | | `name` | string | | Title of the route file. | 839 | | `description` | string | | Descriptive, multiline text. | 840 | | `paths` | [\[path\]]() | | Array of paths that are part of the route. | 841 | | `pois` | [\[path\]]() | | Colleciton of points of interest. | 842 | | `ref` | string | | Reference URL. | 843 | 844 | ### Path 845 | 846 | The path is a representation of a single, uninterrupted track within a route, presented as a colored line with a short name that describes it. 847 | 848 | > Example of a short path that is made of six waypoints: 849 | 850 | ```javascript 851 | { 852 | "waypoints": [ 853 | { "lt": 36.486331, "ln": 136.756827 }, 854 | { "lt": 36.486331, "ln": 136.756827 }, 855 | { "lt": 36.486331, "ln": 136.756827 }, 856 | { "lt": 36.486331, "ln": 136.756827 }, 857 | { "lt": 36.486331, "ln": 136.756827 }, 858 | { "lt": 36.486331, "ln": 136.756827 } 859 | ], 860 | "name": "Main Cycling Path", 861 | "color": "#F7C948" 862 | } 863 | ``` 864 | 865 | | Key | Type | Req. | Description | 866 | | :--- | :--- | :---: | :--- | 867 | | `waypoints` | [\[location\]]() | ✓ | An array of ordered coordinates that make up the path. | 868 | | `name` | string | | Short title. | 869 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 870 | 871 | Order of coordinates in `waypoints` is essential. The path is made by connecting previous waypoint to the current one. After that cursor is advanced to the next waypoint and process is repeated until the waypoints array is exhausted. 872 | 873 | Each path is uninterrupted. User can use multiple paths with the same `name` and `color` if such behavior is required. 874 | 875 | Note that horizontal and vertical accuracy values \(`ha` and `va` respectively\) in location, when used describing path will be ignored. Path objects must have accurate coordinates. 876 | 877 | ### POI 878 | 879 | POI, or Point of Interest, is a representation of a single place on the route, presented as a color dot with a short name that describes it. POIs are designed to be glanceable and straightforward during the activity. 880 | 881 | > Example of a café near Kanazawa: 882 | 883 | ```javascript 884 | { 885 | "coordinate": { 886 | "lt": 36.486331, 887 | "ln": 136.756827 888 | }, 889 | "name": "1st Coffee Stop", 890 | "color": "#E67635" 891 | } 892 | ``` 893 | 894 | | Key | Type | Req. | Description | 895 | | :--- | :--- | :---: | :--- | 896 | | `coordinate` | [location]() | ✓ | Coordinate of the place. | 897 | | `name` | string | | Short title. | 898 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 899 | 900 | Note that horizontal and vertical accuracy values \(`ha` and `va` respectively\) in location, when used describing POI will be ignored. POI objects must have accurate coordinates. 901 | 902 | ## Common Types 903 | 904 | ### DateTime 905 | 906 | DateTime, date, or just `t`, should be specified as a string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format or [UNIX Time](https://en.wikipedia.org/wiki/Unix_time). 907 | 908 | In cases where DateTime repeats frequently, or sub-second precision is required, the encoder should use UNIX time format. It is a good practice to use ISO 8601 in all cases but in [samples](), where **UNIX Time** is advantageous as the value of `t`. 909 | 910 | ### Sport 911 | 912 | Following sports are suppoted by fitness·json: 913 | 914 | | Value | Description | 915 | | :--- | :--- | 916 | | `run` | Runnning | 917 | | `cycle` | Cycling on a two-wheeled bicycle | 918 | | `swim` | Any style of swimming | 919 | | `walk` | Walking. Maybe a slow-paced stroll, tracking, or racewalking. | 920 | | `transition` | Activity performed in between two different disciplines, as in triathlon. Should be treated synonymous to running when no accessibility options are present. | 921 | | `wheelchair` | Race pace wheelchair | 922 | | `handcycle` | Handcycling | 923 | 924 | Note that `wheelchair` and `handcycle` are distinct types of activity and must not be replaced by `run` and `cycle` respectively. fitness·json format respects para-sport as-is and treats them as first-class citizens. 925 | 926 | ### Environment 927 | 928 | The environment of activity. 929 | 930 | | Value | Description | 931 | | :--- | :--- | 932 | | `indoor` | Performed indoors. Usually means that GPS data is not available or should be ignored. | 933 | | `outdoor` | Performed outdoors, where GPS information and atmospheric conditions matter or provide context. | 934 | | `virtual` | Performed in vistual environments such as computer games, simulations, etc. | 935 | 936 | ### Sex 937 | 938 | Biological sex may be useful when calculating calorie burn during the workouts. Ideally, this metric should be highly optional and not required Possible values for sex are: 939 | 940 | | Value | Description | 941 | | :--- | :--- | 942 | | `unknown` | **Default** value | 943 | | `male` | Male | 944 | | `female` | Female | 945 | | `other` | Athlete explicitly declined to provide sex information. | 946 | 947 | **Difference beween `unknown` and `other`** 948 | 949 | Value of `unknown` is assumed as the default value when the athlete does not provide sex. This value of `unknown` grants application to make an educated guess of the athlete's biological sex if it is beneficial to the data analysis. Value of `other`, explicitly tells the decoder that sex-based decisions must be avoided as they are inaccurate or not appropriate for the athlete. 950 | 951 | ### Author 952 | 953 | > Example of complete author object: 954 | 955 | ```javascript 956 | { 957 | "name": "Joana Appleseed", 958 | "email": "joanaappleseed@example.com", 959 | "link": "https://leopardskins-cyclingclub.org", 960 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 961 | } 962 | ``` 963 | 964 | Auhtor, or person object describes and indetifies a person and her contact information. 965 | 966 | | Key | Type | Req. | Description | 967 | | :--- | :--- | :---: | :--- | 968 | | `name` | string | | Full name of a person. | 969 | | `email` | string | | Contact email of a person. | 970 | | `link` | string | | Home web page, blog, or social account. | 971 | | `profile_picture_url` | string | | URL string of the profile image. Should not be a large file. | 972 | 973 | ### Comment 974 | 975 | > Example of comment object: 976 | 977 | ```javascript 978 | { 979 | "date": "2019-04-21T04:37:13+00:00", 980 | "text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 981 | "author": { 982 | "name": "Joana Appleseed", 983 | "email": "joanaappleseed@example.com", 984 | "link": "https://leopardskins-cyclingclub.org", 985 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 986 | } 987 | } 988 | ``` 989 | 990 | Auhtor, or person object describes and indetifies a person and her contact information. 991 | 992 | | Key | Type | Req. | Description | 993 | | :--- | :--- | :---: | :--- | 994 | | `date` | datetime | ✓ | Date comment was created | 995 | | `text` | string | ✓ | Contents of the comment | 996 | | `author` | [author]() | | Author of the comment. | 997 | 998 | When `author` is not present in comment object, the decoder should assume the athlete herself authored a comment as a post-workout note. 999 | 1000 | -------------------------------------------------------------------------------- /version/1.0.0-alpha.4.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Complete specification and documentation of fitness·json file format family. 3 | --- 4 | 5 | # 1.0.0-alpha.4 6 | 7 | fitness·json is a family of data interchange objects based on JavaScript Object Notation \(JSON\) designed specifically for the storing and sharing of sport, fitness, and health data. It defines several types of JSON objects and how they combine to represent data about athlete's workouts, fitness profile, and guided workout instructions. 8 | 9 | Fitness JSON ecosystem is designed for endurance athletes such as runners, cyclists, triathletes, para-triathletes, and alike. The format of the data is friendly to both—computers and humans—and can be easily explored, adjusted, and extended. 10 | 11 | {% hint style="danger" %} 12 | This is an alpha version of the fitness·json. Things will break, frequently. Spec is not ready for the prime time, or usage in production software. 13 | {% endhint %} 14 | 15 | ### Requirements Language 16 | 17 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119). 18 | 19 | ### File Format 20 | 21 | Fitness JSON objects can be stored in persistent storage as files. Such self-contained objects must have `version` key at the very top of the root object. 22 | 23 | Files should be pretty-printed as it is friendlier to humans. In cases where larger file sizes are an issue, the content may be compacted by stripping the whitespace. 24 | 25 | #### File Extensions 26 | 27 | `.json` is highly recommended. When this causes issues, `.fitjson` is a sensible fallback. 28 | 29 | #### Macintosh UTIs 30 | 31 | | File Type | UTI | Parent UTI | 32 | | :--- | :--- | :--- | 33 | | Workout | `org.fitnessjson.workout` | `public.json` | 34 | | Guide | `org.fitnessjson.guide` | `public.json` | 35 | | Route | `org.fitnessjson.route` | `public.json` | 36 | 37 | ### Wordmark and Correct Spelling 38 | 39 | **fitness·json** is a wordmark of the format and must be spelled with `·` [middle dot](https://en.wiktionary.org/wiki/%C2%B7) glyph between words. Capitalization must be avoided, even if the sentence starts with the wordmark. Whenever usage of the wordmark is impossible or not desired, **Fitness JSON** is a recommended alternative \(note the capital F and JSON\). 40 | 41 | ## Workout 42 | 43 | The workout object is self-contained and can stored in a single file. The purpose of the workout object is to store all recorded activities as a single workout along with its completion status, and optionally, post-workout comments, athlete's profile, and guided workout used as an instruction for recorded activities. 44 | 45 | ### Top Level 46 | 47 | Example of the completed workout: 48 | 49 | ```javascript 50 | { 51 | "version": "https://fitnessjson.org/version/1.0.0-alpha.4#workout", 52 | "title": "2019-04-29 [T1] Tempo Run (interrupted)", 53 | "status": "complete", 54 | "activities": [ 55 | { 56 | "sport": "cycle", 57 | "env": "outdoor", 58 | "start_date": "date", 59 | "samples": [ 60 | { 61 | "t": 123123123, 62 | "hr": 84, 63 | "p": 189 64 | }, 65 | { 66 | "t": 123123123, 67 | "hr": 85, 68 | "p": 174 69 | }, 70 | { 71 | "t": 123123123, 72 | "hr": 83, 73 | "p": 182 74 | } 75 | ], 76 | "laps": [ 77 | { "t": 1235323, "trigger": "user" } 78 | ] 79 | }, 80 | { 81 | "sport": "run", 82 | "env": "outdoor", 83 | "start_date": "date", 84 | "samples": [ 85 | { 86 | "t": 123123123, 87 | "hr": 110, 88 | } 89 | ] 90 | } 91 | ] 92 | } 93 | ``` 94 | 95 | The typical workout object contains an array of **activities**, valid **status** value, and **version**. 96 | 97 | Note that the workout's start date is `start_date` of its first activity. Workout with empty `activities` and the status of anything other than `empty` is invalid. 98 | 99 | | Key | Type | Req. | Description | 100 | | :--- | :--- | :---: | :--- | 101 | | `version` | string | ✓ | The URL of the version of the format the object or file. | 102 | | `title` | string | | Title of the workout for easy identification. | 103 | | `activities` | \[activity\] | ✓ | An array of activities that are part of the workout. If the array has more that one activity, the workout is considered to be a multisport \_\_workout. | 104 | | `status` | status | ✓ | The completion status of the workout. | 105 | | `comments` | \[comment\] | | Post-workout comments. May contain many **comments** from various **people**. | 106 | | `profile` | profile | | The snapshot of the profile at the time of the workout completion. | 107 | | `guide` | guide | | The snapshot of the performed guide. Use with caution—athlete may not want to share this. | 108 | | `route` | route | | The snapshot of the route that the athlete used during the workout. Use with caution—athlete may not want to share this. | 109 | 110 | #### Triathlon and Multisport Transitions 111 | 112 | Triathlon and multisport transitions are distinct activities. Such objects have `sport: transition` key-value pair present. 113 | 114 | ### Workout Status 115 | 116 | Workouts may have one of following statuses depending on circumstances uppon its completion. 117 | 118 | | Value | Description | 119 | | :--- | :--- | 120 | | `empty` | The workout is newly created or has no meaningful data. | 121 | | `incomplete` | The workout has incomplete data. An athlete should have an opportunity to carry-on recording within a reasonable timeframe after recording was interupted. | 122 | | `complete` | **Default** value. The workout is complete and ready for analysis. | 123 | 124 | #### Causes of Incomplete Workout 125 | 126 | Incomplete workouts can exist for several reasons. For instance, a recorder malfunction. Consider the following:‌ 127 | 128 | 1. The recorder should write a workout object to persistent storage periodically to minimize data loss in case of a fatal crash. 129 | 2. The recorder must set the `status` key to `incomplete` while recording. Only after an athlete explicitly finishes the workout \(or guided workout is complete\) `status` must be reset to `complete`. 130 | 3. When `status` is `incomplete`, and the recorder is not running, an athlete should have an opportunity to restore the recording state and carry-on activity within a reasonable time interval after the last sample. 131 | 132 | ### Embedded Profile 133 | 134 | Example of a workout with an embedded profile: 135 | 136 | ```javascript 137 | { 138 | "version": "https://fitnessjson.org/version/1.0.0-alpha.4#workout", 139 | "activities": [{ }], // redacted 140 | "status": "complete", 141 | "profile": { 142 | "lthr": 169, 143 | "ftp": 220, 144 | "vo2max": 39, 145 | "weight": 74.4, 146 | } 147 | } 148 | ``` 149 | 150 | An athletes' fitness level and body measurements change over time, hence the variable nature of **profile** over time. To provide a better context for the workout during the overview or analysis, athletes may include their profile in the workout object for historical reference. 151 | 152 | For instance, let's imagine an athlete preparing for an upcoming cycling event. Her weight two weeks ago was different from the current due to race preparation. Because of that, when she's analyzing data from two weeks ago, her `w/kg` power must be calculated from the embedded profile from that time. This guarantees measurements to match reality at the time of the recording. 153 | 154 | ### Sharing Workout: Best Practices 155 | 156 | Information stored in workout objects and files can be highly sensitive, both from the privacy perspective, as well as the athlete's competitive advantage. The sharer should not share Fitness JSON objects as-is unless the athlete explicitly consents or triggers such action. 157 | 158 | ## Activity 159 | 160 | Example of redacted activity object: 161 | 162 | ```javascript 163 | { 164 | "sport": "cycle", 165 | "env": "outdoor", 166 | "start_date": "date", 167 | "samples": [ ], 168 | "laps": [ ], 169 | "markers": [ ] 170 | } 171 | ``` 172 | 173 | The activity object describes one activity of a particular sport performed in a single environment. Examples of activity are _Outdoor Run_, _Indoor Cycle_, _Outdoor Handcycle_, _Open-water Swim_, and _Outdoor \[Triathlon\] Transition_. An ordered sequence of activities forms a multi-sport workout. However, most workouts may contain only a single activity. 174 | 175 | Additionally, activity objects contain recorded data. Data collected from various sensors and data providers must be stored sequentially as **samples**. Activities may also contain **laps** and **markers**. 176 | 177 | #### Top Level 178 | 179 | The typical activity contains **sport**, **environment**, **start date**, and an array of **samples**. 180 | 181 | | Key | Type | Req. | Description | 182 | | :--- | :--- | :---: | :--- | 183 | | `version` | string | ✓ | The URL of the version of the format the object. | 184 | | `sport` | sport | ✓ | The sport of the activity. | 185 | | `env` | environment | ✓ | The environment of the activity. | 186 | | `start_date` | date | ✓ | Date when activity is started. This date may not match `t` value from the first sample, as data from the sensor may be delayed. | 187 | | `samples` | \[sample\] | ✓ | Samples recorded during the activity ordered chronologically, oldest to newest. | 188 | | `laps` | \[lap\] | | Array of all laps. | 189 | | `markers` | \[marker\] | | Array of user markers. | 190 | | `options` | \[string\] | | Set of options that hint parser about how to approach data. | 191 | 192 | ### Activity Options 193 | 194 | ```javascript 195 | { 196 | "sport": "cycle", 197 | "env": "outdoor", 198 | "options": ["raw", "PTVIx"], 199 | "start_date": "2019-04-20T04:20:42+00:00", 200 | "samples": [ 201 | // { ... } 202 | ], 203 | } 204 | ``` 205 | 206 | #### General Options 207 | 208 | | Value | Description | 209 | | :--- | :--- | 210 | | `raw` | Tells parser that data is not processed and might need smoothing. This usually indicates that the recorder didn't apply any filters when recording data from sensors. | 211 | | `user_adjusted` | The contents of the activity are adjusted or modified by the user. Workout data should be presented as-is. | 212 | 213 | {% hint style="info" %} 214 | If the options key is missing or is empty, the parser must assume that data is presented in a raw state. Presented data might need filtering or smoothing. 215 | {% endhint %} 216 | 217 | #### Accessibility Options 218 | 219 | Some activities can not be described completely using environment and sport alone. For such cases, `options` should provide more information. 220 | 221 | | Value | Description | 222 | | :--- | :--- | 223 | | `PTWCx` | Athlete uses a recumbent handcycle on the bike course and a racing wheelchair on the run segment \(in context of triathlon.\) | 224 | | `PTSx` | In both bike and run segments \(in context of triathlon,\) amputee athletes use prosthesis or other supportive devices. | 225 | | `PTVIx` | Athlete rides a tandem. | 226 | 227 | ### Sample 228 | 229 | The sequence of ordered samples is the foundation of successful data analysis. 230 | 231 | Annotated example of a sample: 232 | 233 | ```javascript 234 | { 235 | "t": 1555678832, // datetime, unix epoch time 236 | "hr": 80, // heart rate, bpm 237 | "s": 1.4, // speed, m/s 238 | "l": { // location object 239 | "lt": 36.486331, // latitude 240 | "ln": 136.756827, // longitude 241 | "ha": 5 // horizontal accuracy, meters 242 | }, 243 | "p": 200, // power, watts 244 | "pb": 0.54, // power left-right ratio 245 | "el": 0.2, // elevation delta, meters 246 | "alt": 2314.2, // altitude, meters 247 | "grd": 2.1, // grade, % slope 248 | "at": 24.7, // temperature, celsius 249 | "d": 3.12, // displacement, meters 250 | "cd": 5001.4 // cumulative distance, meters 251 | "c": 80, // cadecne, rpm 252 | "crs": 98.34 // course, degrees 253 | } 254 | ``` 255 | 256 | | Key | Type | Unit | Description | 257 | | :--- | :--- | :---: | :--- | 258 | | `t` | datetime | | The moment of data capture. | 259 | | `hr` | int | BPM | Heart rate measurement | 260 | | `l` | location | | Geographical location of an athlete. | 261 | | `p` | double | W | Total power measurement. | 262 | | `pb` | double | | Power bias, or distributions between left and right limbs. Must be ignored if the sample has no `p` measurement. | 263 | | `c` | int | rpm | Cadence measurement. | 264 | | `s` | double | m/s | Speed measurement. | 265 | | `crs` | double | ºN | Course is direction in which the athlete is traveling, measured in degrees and relative to due north. | 266 | | `d` | double | m | Displacement is distance from the last sample that contains `d` key. | 267 | | `cd` | double | m | Cumulative distance is total distance covered from the start of the activity. | 268 | | `alt` | double | m | Absolute altitude of an athlete from sea level. | 269 | | `el` | double | m | Elevation is the altitude delta from the beginning of the activity. | 270 | | `grd` | double | % | Grade of the slope athlete is on. | 271 | | `at` | double | ºC | Ambient temperature measurement. | 272 | 273 | An empty sample is considered to be invalid and must be ignored. Non-empty sample must contain **`t` and at least one other measurement**. Sample containing the only `t` is also invalid and must be ignored. 274 | 275 | #### Location 276 | 277 | Example of a location object with a latitude, longitude, horizontal accuracy of 30 centimeters and vertical accuracy of 5 meters: 278 | 279 | ```javascript 280 | { 281 | "lt": 35.679687, 282 | "ln": 139.757583, 283 | "ha": 0.3, 284 | "va": 5, 285 | } 286 | ``` 287 | 288 | The location object describes a geographical location on a 2D plane. The location object may include 2D or 3D precision of the location. Precision values may help data analysis software to calculate an athlete's relative path in a more precise manner. 289 | 290 | | Key | Type | Req. | Unit | Description | 291 | | :--- | :--- | :---: | :---: | :--- | 292 | | `lt` | double | ✓ | degrees | The latitude in degrees | 293 | | `ln` | double | ✓ | degrees | The longitude in degrees | 294 | | `ha` | double | | m | Horizontal accuracy | 295 | | `va` | double | | m | Vertical accuracy | 296 | 297 | It is recommended to store unprocessed location data from the sensors if it is with a reasonable noise to signal ratio. Smoothing and other filtering techniques may be applied uppon data presentation. 298 | 299 | #### Displacement and Cumulative Distance 300 | 301 | Unlike most fitness file formats, Fitness JSON supports two types of measurements to indicate the distance athlete is covering. Depending on various conditions, the decoder might decide to use either cumulative distance or calculate data from the displacements. Both data points can be used to estimate a more accurate data uppon presentation. 302 | 303 | #### Latitude and Longitude Units 304 | 305 | Positive values indicate latitudes north of the equator. Negative values indicate latitudes south of the equator. 306 | 307 | #### Cadence Units 308 | 309 | The unit of cadence depends on the **sport**. For simplicity's sake, it is generalized as RPM \(revolutions per minute\). However, it could mean SPM \(steps per minute\) for running, and PPM \(pushes per minute\) for a wheelchair. 310 | 311 | #### Power Bias 312 | 313 | The value of power bias, or power distribution, is a ratio of work between athlete's left and right limbs. Value of `0.0` means that the athlete's right limb is doing all of the work and value of `1.0` means that the athlete's left limb is doing all of the work. 314 | 315 | ```javascript 316 | { 317 | "t": 1555750866, 318 | "p": 234, 319 | "pb": 0.53 320 | } 321 | ``` 322 | 323 | For instance, imagine an athlete producing 234 watts on a dual-side-power-meter-equipped bicycle. The power bias value is 0.53, meaning the athlete is producing 124W \(53% of the total power\) with the left leg and 110W \(47%\) with the right leg. ‌ 324 | 325 | A sample may not have a power bias object while having value for `p`. Such a case, as an example, maybe be when the bicycle has left-only power meter. 326 | 327 | ### Lap 328 | 329 | Typical lap event triggered by an athlete: 330 | 331 | ```javascript 332 | { 333 | "t": "2019-04-20T04:20:42+00:00", 334 | "trigger": "manual" 335 | } 336 | ``` 337 | 338 | Lap object describes an event. Lap events can be triggered manually by an athlete, or automatically by fitness equipment or the **guide**. 339 | 340 | | Key | Type | Req. | Description | 341 | | :--- | :--- | :---: | :--- | 342 | | `t` | datetime | ✓ | Datetime of the event triggered | 343 | | `trigger` | lap\_trigger | | Describes what or who triggered an event. | 344 | 345 | #### Lap Trigger 346 | 347 | The value of `lap_trigger` describes how the lap event was initiated. 348 | 349 | | Value | Description | 350 | | :--- | :--- | 351 | | `unknown` | **Default** value. Assumed when no `trigger` is present in lap object. | 352 | | `manual` | Athlete pushed _Lap_ button. | 353 | | `distance` | Distance based auto-lap feature of an app. | 354 | | `timer` | Time based auto-lap feature of an app. | 355 | | `position` | Geographical position or fence. | 356 | | `equipment` | Fitness equipment requested start of new lap. | 357 | | `guide` | **Guided workout** proceeded to next interval or block. | 358 | 359 | ### Marker 360 | 361 | Simple marker noting a particular moment: 362 | 363 | ```javascript 364 | { 365 | "t":"2019-04-20T04:20:42+00:00" 366 | } 367 | ``` 368 | 369 | Marker with a note: 370 | 371 | ```javascript 372 | { 373 | "t":"2019-04-20T04:20:42+00:00", 374 | "note": "Feels like I've hit the wall." 375 | } 376 | ``` 377 | 378 | Marker object describes a specific point in time of the activity for the later reference. Marker object `t` value can be mapped to the corresponding sample and its measurements by the data analysis software. 379 | 380 | | Key | Type | Req. | Description | 381 | | :--- | :--- | :---: | :--- | 382 | | `t` | datetime | ✓ | Moment of time to mark. | 383 | | `trigger` | string | | Short note. | 384 | 385 | ## Guide 386 | 387 | Guide, or Guided Workout, is an object or file with a set of instructions on how the athlete should perform the activity. Guides are great tools for structured workouts as they can be as vague or as strict as the guide author desires. 388 | 389 | ### Anatomy of Guide 390 | 391 | There are a couple of essential objects that make up a single guide. 392 | 393 | 👉 TODO: Visualization of the guide, its intervals, and blocks. 394 | 395 | Guides consist of **intervals** and **blocks**. Intervals are groups of blocks. Blocks are atomic objects that describe a single step of work in the guide. Ultimatly guide is a sequence of blocks grouped by intervals with the shared environment, sport, and repeat count. 396 | 397 | #### Top Level 398 | 399 | ```javascript 400 | { 401 | "version": "https://fitnessjson.org/version/1.0.0-alpha.4#gudie", 402 | "title": "Tempo Ride", 403 | "symbol": "T1", 404 | "symbol_color": "#FF9900", 405 | "intervals": [ 406 | { 407 | // ... 408 | } 409 | ], 410 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 411 | "author": { 412 | "name": "John Appleseed", 413 | "email": "johnappleseed@example.com", 414 | "link": "https://leopardskins-cyclingclub.org" 415 | }, 416 | "created_at": "2019-04-21T00:41:57+00:00", 417 | "ref": "https://leopardskins-cyclingclub.org/guides/" 418 | } 419 | ``` 420 | 421 | Non-empty guides must have at least one interval with at least one block. Otherwise, the guide is considered to be empty. The empty guide is not suitable for the activity recoding. 422 | 423 | | Key | Type | Req. | Description | 424 | | :--- | :--- | :---: | :--- | 425 | | `version` | string | ✓ | URL of the version of the format of the object or file. | 426 | | `title` | string | | Title of the guide. | 427 | | `symbol` | string | | Abbreviation or shorthand code for easy guide identification. No more than two characters. | 428 | | `symbol_color` | string | | Color of the symbol for easy guide identification. A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 429 | | `description` | string | | A relatively long-form description of the guide. An excellent place to inform the athlete what this guide is trying to achieve, what to expect during the workout, and any other information that may be useful for the athlete before she starts recording the workout. | 430 | | `intervals` | interval | ✓ | An array of ordered intervals. | 431 | | `author` | author | | Name and contact information of the author of this guide. | 432 | | `created_at` | datetime | ✓ | The date and time when guide was created. | 433 | | `ref` | string | | Reference URL pointing to any auxiliary or related information. | 434 | 435 | ### Interval 436 | 437 | Example of interval with two blocks that will be repeated twice in the workout. 438 | 439 | ```javascript 440 | { 441 | "title": "Max Power", 442 | "env": "outdoor", 443 | "sport": "run", 444 | "repeats": 2, 445 | "blocks": [ 446 | { 447 | // ... 448 | }, 449 | { 450 | // ... 451 | } 452 | ], 453 | "description": "Maximum effort followed by 2 min rest. Two sets. Go!" 454 | } 455 | ``` 456 | 457 | Interval objects provide structure to the guide. They group related blocks, as well as inform the guide about blocks' environment and sport. Intervals also can have repeat count, thus eliminating the need to have duplicate groups of blocks in sequence. 458 | 459 | | Key | Type | Req. | Description | 460 | | :--- | :--- | :---: | :--- | 461 | | `repeats` | int \[1..∞\) | | The number of repeats. Describes how many times a guide-player repeats a given group of blocks during a workout. | 462 | | `title` | string | | Title of the interval. | 463 | | `sport` | sport | ✓ | Sport | 464 | | `env` | environment | ✓ | Environment | 465 | | `blocks` | block | ✓ | An array of ordered blocks that are part of this interval. | 466 | | `description` | string | | Short instruction and overview of the block. | 467 | 468 | #### Repeats 469 | 470 | When `repeat` has a value greater than `1`, the guide should put the interval's first block in the queue right after its last block. Only after all blocks in the interval have been performed `repeat` times, the guide player must advance to the next interval \(first block of the next interval\). This process should repeat until the guide has run out of blocks and their parent intervals. 471 | 472 | The value of `repeat` is optional, and when absent from the object, the decoder should assume the value of `1`. Values less than `1` are invalid. 473 | 474 | ### Block 475 | 476 | ```javascript 477 | { 478 | "type" : "work", 479 | "duration" : { 480 | "condition" : "gte", 481 | "trigger" : "distance", 482 | "value" : 5000 483 | }, 484 | "target_type" : "rvalue", 485 | "target_rvalue" : { 486 | "anchor" : "threshold", 487 | "lower" : 0.95, 488 | "upper" : 1.05, 489 | "metric" : "heart_rate" 490 | }, 491 | "description" : "Run for 5K. Try to be near your LTHR.", 492 | } 493 | ``` 494 | 495 | The block object describes a single unit of work in the guide. 496 | 497 | | Key | Type | Req. | Description | 498 | | :--- | :--- | :---: | :--- | 499 | | `type` | type | ✓ | Type of the block. Describes the kind of work to perform. | 500 | | `duration` | duration | ✓ | The condition upon meeting which block is complete and guide advances to the next block. | 501 | | `target_type` | target\_type | ✓ | Type of the target. | 502 | | `target_value` | value\_target | | The object describing target when `target_type` is `value.` | 503 | | `target_rvalue` | rvalue\_target | | The object describing target when `target_type` is `rvalue.` | 504 | | `target_zone` | zone\_target | | The object describing target when `target_type` is `zone.` | 505 | | `description` | string | | Short instruction and overview of the block. | 506 | 507 | Block can be one of _several types_, have completion condition, and optionally, target. Targets itself, can be _one of four types_. 508 | 509 | #### Block Type 510 | 511 | The block type object describes the nature of work in the current block. Those are provided mostly for visual reference. Type of work does not affect completion duration, nor the target. 512 | 513 | | Value | Description | 514 | | :--- | :--- | 515 | | `warmup` | Warm-up | 516 | | `work` | Relatively hi-intensity activity. | 517 | | `rest` | Lower intensity activity. | 518 | | `cooldown` | Cool down. | 519 | 520 | ### Block Completion 521 | 522 | The guide can advance by automatically or manually. The block completion object describes exact instructions on how the block should advance to the next block in the guide. 523 | 524 | | Key | Type | Req. | Description | 525 | | :--- | :--- | :---: | :--- | 526 | | `trigger` | trigger | ✓ | What type of measurement or event triggers advancement to the next block. | 527 | | `condition` | condition | | What conditions should be satisfied by the value of the trigger. | 528 | | `value` | double | | Value of measurement based on what condition is selected. | 529 | 530 | #### Trigger 531 | 532 | Example of a cool down block that ends when the athlete's heart rate drops below 60 BPM: 533 | 534 | ```javascript 535 | { 536 | "duration": { 537 | "trigger": "heart_rate", 538 | "condition": "lt", 539 | "value": 60 540 | }, 541 | "description": "Run for 5K", 542 | "type": "cooldown", 543 | "target_type": "none" 544 | } 545 | ``` 546 | 547 | Event or measurement that triggers the advancement to the next block or guide completion. When the trigger is `user`, the guide does not advance automatically. It is good practice to remind athletes about manual triggers in the description object of the block. An athlete can always override any other trigger and manually advance to the next block. 548 | 549 | | Value | Description | 550 | | :--- | :--- | 551 | | `user` | Athlete requests next block. | 552 | | `time` | Block timer. | 553 | | `distance` | Block distance. | 554 | | `hr` | Live heart rate. | 555 | | `block_hr` | Block average heart rate. | 556 | | `speed` | Live speed or pace. | 557 | | `block_speed` | Block average speed or pace. | 558 | | `cadence` | Live cadence. | 559 | | `power` | Live power. | 560 | | `power3` | Live power average of last 3 seconds. | 561 | | `power10` | Live power average of last 10 seconds. | 562 | | `power30` | Live power average of last 30 seconds. | 563 | | `block_power` | Block average power. | 564 | 565 | #### Condition 566 | 567 | Example of the block that advances to the next block after 5K: 568 | 569 | ```javascript 570 | { 571 | "duration": { 572 | "trigger": "distance", 573 | "condition": "gte", // note `gte` instead of `eq` 574 | "value": 5000 575 | }, 576 | "description": "Run for 5K", 577 | "type": "work", 578 | "target_type": "none" 579 | } 580 | ``` 581 | 582 | You could read out loud above block's completion condition like so: 583 | 584 | > _“This block will be complete when athlete covers the **distance** that **is greater than or equal** to **5000 meters**.”_ 585 | 586 | The condition object describes an operator applied to the measurement and value of value object in the block completion. 587 | 588 | | Value | Symbol | Description | 589 | | :--- | :---: | :--- | 590 | | `lt` | < | Less than. | 591 | | `lte` | ≤ | Less than or equal to. | 592 | | `eq` | = | Strictly equal to. | 593 | | `gte` | ≥ | Greater than or equal to. | 594 | | `gt` | > | Greater than. | 595 | 596 | {% hint style="info" %} 597 | Equality \(`eq`\) is strict equality. When designing blocks that are complete at after 5000 meters, guide designers must use greater than or equal to condition operator instead of equal to as recorder might sample distance values just before and after athlete passes 5K mark. Neither 4998.9, nor 5000.4, are strictly equal to 5000, resulting in a never-ending block. 598 | {% endhint %} 599 | 600 | ### Block Targets 601 | 602 | Example block without a target has a duration of two minutes: 603 | 604 | ```javascript 605 | { 606 | "type": "work", 607 | "target_type": "none", 608 | "duration": { 609 | "trigger": "time", 610 | "condition": "gte", 611 | "value": 120 612 | }, 613 | "description": "Simply run for two minutes.", 614 | } 615 | ``` 616 | 617 | Block object can have one of four types of targets as described in `target_type`. 618 | 619 | | Value | `object` Key | Description | 620 | | :--- | :--- | :--- | 621 | | `none` | N/A | Block has no target \(a.k.a. _Free Block._\) | 622 | | `value` | `target_value` | The athlete is asked to be in the range of specific absolute numbers for the given **metric.** | 623 | | `rvalue` | `target_rvalue` | The athlete is asked to be the range calculated by multiplying relative values to corresponding fitness **profile** values for the given **metric** and **anchor.** | 624 | | `zone` | `target_zone` | The athlete is asked to hit the zone at the given index. The zone index is independent of the athlete's choice zone set. The guide should specify what zone-set it is designed to work. | 625 | 626 | Unless `none` is a value of `target_type`, the block must have a corresponding target object as its content. 627 | 628 | #### No Target \(Free Block\) 629 | 630 | No target block. The athlete may do whatever they want. This kind of block is useful when actual goal of the block is to just complete the block. 631 | 632 | #### Absolute Value Target 633 | 634 | Example of block with `target_type: value`. An athlete is asked to maintain heart rate within 80–100 BPM range for 20 minutes: 635 | 636 | ```javascript 637 | { 638 | "type": "work", 639 | "target_type": "value", 640 | "target_value": { 641 | "metric": "heart_rate", 642 | "lower": 80, 643 | "upper": 100, 644 | }, 645 | "duration": { "trigger": "time", "condition": "gte", "value": 1200 } 646 | } 647 | ``` 648 | 649 | The absolute-value-target object describes the specific range of values of a given **metric** that an athlete should aim. 650 | 651 | All fields are required. 652 | 653 | | Key | Type | Description | 654 | | :--- | :--- | :--- | 655 | | `metric` | metric | Target metric | 656 | | `lower` | double | Lower bound | 657 | | `upper` | double | Upper bound \(exclusive\) | 658 | 659 | While absolute value targets are straightforward and easy to understand, note that it is infrequent when the same values work for different athletes. If you intend to share the guide online, consider using **Relative Value Target** objects as they calculate absolute ranges based on an athlete's profile. 660 | 661 | #### Relative Value Target 662 | 663 | Example of the block with `target_type: rvalue`. An athlete is asked to maintain 90–110% of her FTP during 2 minutes: 664 | 665 | ```javascript 666 | { 667 | "type": "work", 668 | "target_type": "rvalue", 669 | "target_value": { 670 | "metric": "power", 671 | "anchor": "threshold", 672 | "lower": 0.9, 673 | "upper": 1.1, 674 | }, 675 | "duration": { "trigger": "time", "condition": "gte", "value": 120 } 676 | } 677 | ``` 678 | 679 | Unlike absolute-value-target, relative-value-targets are calculated based on data points from the athlete's **profile**. They can be universal among the people with various fitness levels and automatically update as an athlete's fitness changes over time. As with absolute-value-targets, the target range is bound to `lower` and `upper` values, but instead of exact measurements, they contain _multipliers_. Those values get multiplied by a measurement from the profile based on the combination of values from `metric` and `anchor`. 680 | 681 | For instance, if the value of `metric` is `power` and value of `anchor` is `threshold`, then multipliers from the target's `lower` and `upper` are applied to the **profile**'s `ftp` value. 682 | 683 | All fields are required. 684 | 685 | | Key | Type | Description | 686 | | :--- | :--- | :--- | 687 | | `metric` | metric | Target metric. | 688 | | `anchor` | anchor | Relative to what profile measurement. | 689 | | `lower` | double | Lower bound multiplier. | 690 | | `upper` | double | Upper bound multiplier \(exclusive\). | 691 | 692 | #### Zone Target 693 | 694 | Example of the block with `target_type: zone`. An athlete is asked to maintain heart rate zone 3 for 4 kilometers: 695 | 696 | ```javascript 697 | { 698 | "type": "work", 699 | "target_type": "zone", 700 | "target_value": { 701 | "metric": "heart_rate", 702 | "index": 3, 703 | }, 704 | "duration": { "trigger": "distance", "condition": "gte", "value": 4000 } 705 | } 706 | ``` 707 | 708 | Zone Target object describes which zone athlete should aim for, and what kind of zone it should be. Note that the value of `index` is an index of a zone in an array of zones in a zone-set. Meaning, if an athlete replaces zone set with a different one, a new zone that is located at the `index` becomes the new target zone. 709 | 710 | All fields are required. 711 | 712 | | Key | Type | Description | 713 | | :--- | :--- | :--- | 714 | | `metric` | metric | Type of metric of which zone should be used. | 715 | | `index` | int | Index of a selected zone-set. | 716 | 717 | #### Metric 718 | 719 | The metric object describes what type of measurement target value range is. 720 | 721 | | Value | Description | 722 | | :--- | :--- | 723 | | `heart_rate` | Heart rate | 724 | | `cadence` | Cadence \(RPM for cycling and handcycle; SPM for running; PPM for wheelchair.\) | 725 | | `power` | Power | 726 | | `speed` | Speed or pace. | 727 | 728 | #### Metric Anchor 729 | 730 | The anchor object describes the data in the athlete's **profile** that absolute values get calculated from target range values. 731 | 732 | | Value | Description | 733 | | :--- | :--- | 734 | | `min` | An athlete's minimum measured value for the metric \(e.g., **Resting HR** when `metric: heart_rate`.\) | 735 | | `max` | An athlete's maximum measured value for the metric \(e.g., **MaxHR** when `metric: heart_rate`.\) | 736 | | `threshold` | An athlete's threshold value for the metric \(e.g., **FTP** when `metric: power` and `sport: cycle`.\) | 737 | 738 | There are many combinations of metrics and anchors that do not make sense. For instance, consider minimum power. Minimum power an athlete can produce is always equal to 0 watts \(not pedaling.\) Anchoring a target range on 0 watts is not particularly useful since both lower and upper bounds always equal to 0. 739 | 740 | ## Profile 741 | 742 | Example of an athlete's complete profile: 743 | 744 | ```javascript 745 | { 746 | "version": "https://fitnessjson.org/version/1.0.0-alpha.4#profile", 747 | "name": "John Appleseed", 748 | "email": "johnappleseed@example.com", 749 | "link": "https://johnappleseed.me", 750 | "profile_picture_url": "https://johnappleseed.me/images/avatar.jpg", 751 | "hrr": 48, 752 | "hrmax": 201, 753 | "lthr": 169, 754 | "ftp": 220, 755 | "rftpw": 240, 756 | "vo2max": 39, 757 | "weight": 74.4, 758 | "height": 170, 759 | "swim_test_dist": 1000, 760 | "swim_test_time": 500, 761 | "run_tpace": 4.7, 762 | "birthday": "1988-04-17", 763 | "sex": "male", 764 | "wheelchair": false, 765 | "handcycle": false 766 | } 767 | ``` 768 | 769 | Example of the minimal profile for a cyclist: 770 | 771 | ```javascript 772 | { 773 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2.0#profile", 774 | "lthr": 169, 775 | "ftp": 220, 776 | "vo2max": 39, 777 | "weight": 74.4, 778 | } 779 | ``` 780 | 781 | A profile object describes various athlete's measurements and data points. Profile object features several arbitrary categories: essential body measurements, fitness test results, accessibility information, and contact information. 782 | 783 | A profile object is not a database. Hence it stores no historical information. When faced with a profile without any context, the decoder should assume that the profile has _the latest known data_. When the profile object is part of another object \(for instance, a **workout**\), then the decoder must map profile data onto its parent object data. 784 | 785 | #### Top Level 786 | 787 | Since profile may be embedded into other objects, **all values are optional**. 788 | 789 | | Key | Type | Unit | Description | 790 | | :--- | :--- | :--- | :--- | 791 | | `version` | url | | URL of the version of the format the object. | 792 | | `name` | string | | Full name of an athlete. | 793 | | `email` | string | | Contact email of an athlete. | 794 | | `link` | string | | Website of an athlete. | 795 | | `profile_picture_url` | string | | Profile picture URL of an athlete. | 796 | | `birthday` | datetime | | Date of birth. | 797 | | `sex` | sex | | Biological sex of an athlete. | 798 | | `hrr` | int | BPM | Resting heart rate. | 799 | | `hrmax` | int | BPM | Maximum heart rate. | 800 | | `lthr` | int | BPM | Lactate threshold heart rate, LTHR. | 801 | | `ftp` | int | W | Cycling threshold power, FTP. | 802 | | `rftpw` | int | W | Running functional threshold power, rFTPw. | 803 | | `vo2max` | int | mL/\(kg·min\) | Maximal oxygen consumption of an athlete. | 804 | | `weight` | int | kg | Weight of an athlete. | 805 | | `run_tpace` | double | m/s | Running threshold pace. | 806 | | `wheelchair` | bool | | `true` when athlete is a wheelchair user. No key of this key is equivalent of `false`. | 807 | | `handcycle` | bool | | `true` when athlete is a handcycle user. No key of this key is equivalent of `false`. | 808 | 809 | {% hint style="warning" %} 810 | Developers should pay extra attention when sharing profile to other parties or apps, as it may contain private information, both from user privacy, and competitive perspective. **Do not share a profile object in any form without user consent. Make sure users understand what data is shared on every occasion.** 811 | {% endhint %} 812 | 813 | ## Route 814 | 815 | Example of a route with single POI and two paths: 816 | 817 | ```javascript 818 | { 819 | "version": "https://fitnessjson.org/version/1.0.0-alpha.4#route", 820 | "name": "Sunday Group Ride (Spring 2019)", 821 | "description": "Note for new members: we'll take alternative path starting from 30km point (marked so) to avoid two climbs.", 822 | "paths": [ 823 | { 824 | "waypoints": [ 825 | { "lt": 36.486331, "ln": 136.756827 }, 826 | { "lt": 36.486331, "ln": 136.756827 }, 827 | { "lt": 36.486331, "ln": 136.756827 }, 828 | ], 829 | "name": "Main Cycling Path", 830 | "color": "#F7C948" 831 | }, 832 | { 833 | "waypoints": [ 834 | { "lt": 36.486331, "ln": 136.756827 }, 835 | { "lt": 36.486331, "ln": 136.756827 }, 836 | { "lt": 36.486331, "ln": 136.756827 }, 837 | ], 838 | "name": "Novice Detour", 839 | "color": "#8DED2D" 840 | } 841 | ], 842 | "poi": [ 843 | { 844 | "coordinate": { "lt": 36.486331, "ln": 136.756827 }, 845 | "name": "Coffee Stop", 846 | "color": "#E67635" 847 | } 848 | ], 849 | "ref": "https://leopardskins-cyclingclub.org/ride/1234" 850 | } 851 | ``` 852 | 853 | Routes are geographical information about paths and POIs \(points-of-interest\) used as a map overlay while performing an activity. Routes shouldn't require specific workouts but may have additional information for outdoor activity. For instance, the route object can contain the path of a weekly Sunday bike ride with its alternative loops, rest locations, and cafe stop locations. 854 | 855 | #### Top Level 856 | 857 | | Key | Type | Req. | Description | 858 | | :--- | :--- | :---: | :--- | 859 | | `version` | url | ✓ | URL of the version of the object or file. | 860 | | `name` | string | | Title of the route file. | 861 | | `description` | string | | Descriptive, multiline text. | 862 | | `paths` | \[path\] | | Array of paths that are part of the route. | 863 | | `pois` | \[poi\] | | Array of points of interest. | 864 | | `ref` | string | | Reference URL. | 865 | 866 | ### Path 867 | 868 | The path is a representation of a single and uninterrupted track within a route. It is presented as a colored line with a short name that describes it. 869 | 870 | Example of a short path that is made of six waypoints: 871 | 872 | ```javascript 873 | { 874 | "waypoints": [ 875 | { "lt": 36.486331, "ln": 136.756827 }, 876 | { "lt": 36.486331, "ln": 136.756827 }, 877 | { "lt": 36.486331, "ln": 136.756827 }, 878 | { "lt": 36.486331, "ln": 136.756827 }, 879 | { "lt": 36.486331, "ln": 136.756827 }, 880 | { "lt": 36.486331, "ln": 136.756827 } 881 | ], 882 | "name": "Main Cycling Path", 883 | "color": "#F7C948" 884 | } 885 | ``` 886 | 887 | | Key | Type | Req. | Description | 888 | | :--- | :--- | :---: | :--- | 889 | | `waypoints` | location | ✓ | An array of ordered coordinates that make up the path. | 890 | | `name` | string | | Short title. | 891 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 892 | 893 | The order of coordinates in waypoints is crucial. The path is made by connecting the previous waypoint to the current one. After that, the cursor is advanced to the next waypoint, and the process is repeated until the waypoints array is exhausted. 894 | 895 | Each path is uninterrupted. Users can use multiple paths with the same name and color if such behavior is required. 896 | 897 | {% hint style="warning" %} 898 | Horizontal and vertical accuracy values \(`ha` and `va` \), when used describing path are ignored. Path objects must have accurate coordinates. 899 | {% endhint %} 900 | 901 | ### POI 902 | 903 | POI, or Point of Interest, is a representation of a single place on the route, presented as a color dot with a short name that describes it. POIs are designed to be glance-able and straightforward during the activity. 904 | 905 | Example of a café near Kanazawa, Japan: 906 | 907 | ```javascript 908 | { 909 | "coordinate": { 910 | "lt": 36.486331, 911 | "ln": 136.756827 912 | }, 913 | "name": "1st Coffee Stop", 914 | "color": "#E67635" 915 | } 916 | ``` 917 | 918 | | Key | Type | Req. | Description | 919 | | :--- | :--- | :---: | :--- | 920 | | `coordinate` | location | ✓ | Coordinate of the place. | 921 | | `name` | string | | Short title. | 922 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 923 | 924 | {% hint style="warning" %} 925 | Horizontal and vertical accuracy values \(`ha` and `va` \), when used describing POI will be ignored. POI objects must have accurate coordinates. 926 | {% endhint %} 927 | 928 | ## Common Data Types 929 | 930 | ### DateTime 931 | 932 | DateTime, date, or just `t`, should be specified as a string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format or [UNIX Time](https://en.wikipedia.org/wiki/Unix_time). 933 | 934 | In cases where DateTime repeats frequently, or sub-second precision is required, the encoder should use UNIX time format. It is a good practice to use ISO 8601 in all cases but in **samples**, where UNIX Time is advantageous as the value of `t`. 935 | 936 | ### Sport 937 | 938 | Following sports are supposed by fitness·json: 939 | 940 | | Value | Description | 941 | | :--- | :--- | 942 | | `run` | Running | 943 | | `cycle` | Cycling on a two-wheeled bicycle. | 944 | | `swim` | Any style of swimming. | 945 | | `walk` | Walking. Maybe a slow-paced stroll, tracking, or racewalking | 946 | | `transition` | Activity performed in between two different disciplines, as in triathlon. Should be treated synonymous to running when no accessibility options are present | 947 | | `wheelchair` | Race pace wheelchair | 948 | | `handcycle` | Handcycling | 949 | 950 | {% hint style="danger" %} 951 | Wheelchair and handcycle are distinct types of activity and must not be replaced by `run` and `cycle` respectively. fitness·json format respects para-sport as-is and treats them as first-class citizens. 952 | {% endhint %} 953 | 954 | ### Environment 955 | 956 | The environment activity is performed. 957 | 958 | | Value | Description | 959 | | :--- | :--- | 960 | | `indoor` | Performed indoors. Usually means that GPS data is not available or should be ignored. | 961 | | `outdoor` | Performed outdoors, where GPS information and atmospheric conditions matter or provide context. | 962 | | `virtual` | Performed in virtual environments such as computer games, simulations, etc. GPS data is simulated. | 963 | 964 | ### Sex 965 | 966 | Biological sex may be useful when calculating calorie burn during the workouts. Ideally, this metric should be highly optional and not required. Possible values are: 967 | 968 | | Value | Description | 969 | | :--- | :--- | 970 | | `unknown` | **Default** value | 971 | | `male` | Male | 972 | | `female` | Female | 973 | | `other` | Athlete explicitly declined to provide sex information. | 974 | 975 | **Difference between `unknown` and `other`** 976 | 977 | Value of `unknown` is assumed as the default value when the athlete does not provide sex. This value of `unknown` grants application to make an educated guess of the athlete's biological sex if it is beneficial to the data analysis. Value of `other`, explicitly tells the decoder that sex-based data analysis must be avoided as they are inaccurate or not appropriate for the athlete. 978 | 979 | ### Author / Person 980 | 981 | Example of complete author object: 982 | 983 | ```javascript 984 | { 985 | "name": "Joana Appleseed", 986 | "email": "joanaappleseed@example.com", 987 | "link": "https://leopardskinz-cyclingclub.org", 988 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 989 | } 990 | ``` 991 | 992 | Author, or **person**, object describes and identifies a person and their contact information. 993 | 994 | | Key | Type | Req. | Description | 995 | | :--- | :--- | :---: | :--- | 996 | | `name` | string | | Full name of a person. | 997 | | `email` | string | | Contact email of a person. | 998 | | `link` | string | | Home web page, blog, or social account. | 999 | | `profile_picture_url` | string | | URL string of the profile image. Should not be a large file. | 1000 | 1001 | ### Comment / Note 1002 | 1003 | Example of comment object: 1004 | 1005 | ```javascript 1006 | { 1007 | "date": "2019-04-21T04:37:13+00:00", 1008 | "text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 1009 | "author": { 1010 | "name": "Joana Appleseed", 1011 | "email": "joanaappleseed@example.com", 1012 | "link": "https://leopardskins-cyclingclub.org", 1013 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 1014 | } 1015 | } 1016 | ``` 1017 | 1018 | Comment, or **note**, object describes and short textual memo that can be attached to various objects, usually **workouts**. 1019 | 1020 | | Key | Type | Req. | Description | 1021 | | :--- | :--- | :---: | :--- | 1022 | | `date` | datetime | ✓ | Date memo was created. | 1023 | | `text` | string | ✓ | Contents of the memo. | 1024 | | `author` | author | | Author of the memo. | 1025 | 1026 | When `author` is not present in the comment object, the decoder should assume the athlete herself authored a comment as a post-workout note. 1027 | 1028 | -------------------------------------------------------------------------------- /version/1.0.0-alpha.3.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Complete specification and documentation of fitness·json file format family. 3 | --- 4 | 5 | # 1.0.0-alpha.3 6 | 7 | fitness·json is a family of data interchange objects based on JavaScript Object Notation \(JSON\) designed specifically for the storing and sharing of sport, fitness, and health data. It defines several types of JSON objects and how they are combined to represent data about athlete's workouts, fitness profile, and guided workout instructions. 8 | 9 | The whole ecosystem is designed for endurance athletes such as runners, cyclists, triathletes, para-triathletes, and alike. Format of the data is friendly to both—computers and humans—and can be easily explored, adjusted, and extended. 10 | 11 | {% hint style="danger" %} 12 | This is an alpha version of the fitness·json. Things will break, frequently. Spec is not ready for the prime time, or usage in production software. 13 | {% endhint %} 14 | 15 | ### Requirements Language 16 | 17 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119). 18 | 19 | ### File Format 20 | 21 | fitness·json objects can be stored to persistent storage as files. Such, self-contained objects must have `version` key at the very top of the root object. 22 | 23 | Files should be pretty-printed as it is friendlier to humans. In cases where larger file sizes are an issue, the content may be compacted by stripping the whitespaces. 24 | 25 | #### File Extensions 26 | 27 | `.json` is highly recommended. When this cases issues, `.fitjson` is sensible fallback. 28 | 29 | #### Macintosh UTIs 30 | 31 | * Workout file: `org.fitnessjson.workout`, conforms to `public.json`. 32 | * Guide file: `org.fitnessjson.guide`, conforms to `public.json`. 33 | * Route file: `org.fitnessjson.route`, conforms to `public.json`. 34 | 35 | ## Workout 36 | 37 | Workout object is self-contained and can be represented as a single file. Purpose of the workout object is to contain all activities completed as one workout along with its completion status, post-workout comments, and athlete's profile at the moment of workout. 38 | 39 | ### Top Level 40 | 41 | > Example of the completed workout: 42 | 43 | ```javascript 44 | { 45 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#workout", 46 | "title": "2019-04-29 [T1] Tempo Run (interupted)", 47 | "status": "complete", 48 | "activities": [ 49 | { 50 | "sport": "cycle", 51 | "env": "outdoor", 52 | "start_date": "date", 53 | "samples": [ 54 | { 55 | "t": 123123123, 56 | "hr": 84, 57 | "p": 189 58 | }, 59 | { 60 | "t": 123123123, 61 | "hr": 85, 62 | "p": 174 63 | }, 64 | { 65 | "t": 123123123, 66 | "hr": 83, 67 | "p": 182 68 | } 69 | ], 70 | "laps": [ 71 | { "t": 1235323, "trigger": "user" } 72 | ] 73 | }, 74 | { 75 | "sport": "run", 76 | "env": "outdoor", 77 | "start_date": "date", 78 | "samples": [ 79 | { 80 | "t": 123123123, 81 | "hr": 110, 82 | } 83 | ] 84 | } 85 | ] 86 | } 87 | ``` 88 | 89 | Typical workout object contains an array of [activities](../#activity), valid [status](../#activity-options) value, and [version](../#top-level). 90 | 91 | Note that workouts start date is `start_date` of its first activity. Workouts with empty `activities` array and status anything other than `empty` are invalid. 92 | 93 | | Key | Type | Req. | Description | 94 | | :--- | :--- | :---: | :--- | 95 | | `version` | string | ✓ | URL of the version of the format the object and/or file. | 96 | | `title` | string | | Title of the workout for easy identification. | 97 | | `activities` | [\[activity\]](../#activity) | ✓ | An array of activities that are part of the workout. If the array has more that one activity, the workout is considered to be a _multi-sport workout_.\* | 98 | | `status` | [status](../#activity-options) | ✓ | Completion status of the workout. | 99 | | `comments` | [\[comment\]](../#comment) | | Post-workout comments or discussion. May contain many [comments](../#comment) from various [people](../#author). | 100 | | `profile` | [profile](../#profile) | | A snapshot of the profile at the time of workout completion.[‡]() | 101 | | `guide` | [guide](../#guide) | | A snaptshot of the performed guide. Optional; athelete may not want to share this. | 102 | | `route` | [route](../#route) | | A snaptshot of the route if athelete was suing one uppon completion of the workout. Optional; athelete may not want to share this. | 103 | 104 | #### Triathlon and Multisport Transitions 105 | 106 | Triathlon and multisport transitions are distinct activities. Such objects have `sport: transition` key-value pair in them. 107 | 108 | ### Workout Status 109 | 110 | Workouts may have one of several statuses depending on circumstances when workout object was stored. 111 | 112 | | Value | Description | 113 | | :--- | :--- | 114 | | `empty` | The workout is newly created or has no meaningful data. | 115 | | `incomplete` | Workout has incomplete data. An athlete should have an opportunity to carry-on recording data. | 116 | | `complete` | **Default** value. The workout is complete and must not be modified. | 117 | 118 | #### Causes of Incomplete Workout 119 | 120 | Incomplete workouts can exist for several reasons. One of those could be a recorder malfunction. Consider the following when you face such condition: 121 | 122 | 1. Recorder should write workout object to persistent storage periodically so that in case of the fatal crash only a minimal amount of data is lost. 123 | 2. The recorder should set the `status` key to `incomplete` while recording and resets it to `complete` only when an athlete explicitly finishes the workout \(or, guided workout finishes the activity.\) 124 | 3. When `status` is `incomplete`, the athlete should have an opportunity to restore the recording state and carry-on activity within a reasonable time interval after the last sample. 125 | 126 | ### Embeded Profile 127 | 128 | > Example of a workout with an embedded profile: 129 | 130 | ```text 131 | { 132 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#workout", 133 | "activities": [{ }], // redacted 134 | "status": "complete", 135 | "profile": { 136 | "lthr": 169, 137 | "ftp": 220, 138 | "vo2max": 39, 139 | "weight": 74.4, 140 | } 141 | } 142 | ``` 143 | 144 | Athlete's fitness level and body measurements change over time, hence the variable nature of [profile]() over time. To provide better context for the workout, athletes may include their profile in completed workout object for later reference. 145 | 146 | For instance, let's imagine an athlete preparing for an upcoming cycling event. Her weight three weeks ago was different from the current. Because of that, when she's analyzing data from three weeks old workout, her `w/kg` power measurements must be calculated from the embedded profile that possesses her weight at the time of the workout. This guarantees measurements she's looking at match reality at the time of the workout. 147 | 148 | ### Sharing Practices 149 | 150 | Information stored in workout objects and files can be highly sensitive, both from the privacy perspective, as well as the athlete's competitive advantage. 151 | 152 | When sharing workout online or with another party, consider the following: 153 | 154 | * The sharer should not share object as-is unless athlete explicitly consents such action. 155 | * The app should provide an opportunity to omit information from the object before sharing it online. Good candidates for removal are the complete profile, cycling power from every sample, comments, partial profile \(e.g., include `weight`, but omit `email` and `ftp`\). 156 | 157 | ## Activity 158 | 159 | ```javascript 160 | { 161 | "sport": "cycle", 162 | "env": "outdoor", 163 | "start_date": "date", 164 | "samples": [ ], 165 | "laps": [ ], 166 | "markers": [ ] 167 | } 168 | ``` 169 | 170 | Activity object describes one activity of particular sport performed in a single environment. Some examples of activity are _Outdoor Run_, _Indoor Cycle_, _Outdoor Handcycle_, _Open-water Swim_, and _Outdoor \[Triathlon\] Transition_. An ordered sequence of activities put in workout object forms a multi-sport workout. Most workouts contain single activity. 171 | 172 | Apart from describing the environment and sport, activity objects contain recorded data. Data collected from various sensors and data providers must be sequentially stored in [samples]() array. 173 | 174 | Additionally, activities contain [lap events]() and [markers](). 175 | 176 | #### Top Level 177 | 178 | The typical activity contains [sport](), [environment](), start date, and an array of [samples](). 179 | 180 | | Key | Type | Req. | Description | 181 | | :--- | :--- | :---: | :--- | 182 | | `version` | string | ✓ | URL of the version of the format the object. | 183 | | `sport` | [sport]() | ✓ | The sport of the activity. | 184 | | `env` | [environment]() | ✓ | An environment of the activity. | 185 | | `start_date` | [date]() | ✓ | Date when activity is started. This date may not match `t` value from the first sample, as data from the senrsos may be delayed. | 186 | | `samples` | [\[sample\]]() | ✓ | Samples recorded during the activity ordered chronologically. Oldest samples first. | 187 | | `laps` | [\[lap\]]() | | An array of all lap events. Both, automatic and user-generated. | 188 | | `markers` | [\[marker\]]() | | An array of user markers. | 189 | | `options` | [\[string\]]() | | Set of options that hint parser about how to approach data. | 190 | 191 | ### Activity Options 192 | 193 | > Example of activity performed on a tandem bicycle: 194 | 195 | ```javascript 196 | { 197 | "sport": "cycle", 198 | "env": "outdoor", 199 | "options": ["raw", "PTVIx"], 200 | "start_date": "2019-04-20T04:20:42+00:00", 201 | "samples": [ 202 | // { ... } 203 | ], 204 | } 205 | ``` 206 | 207 | #### General Options 208 | 209 | | Value | Description | 210 | | :--- | :--- | 211 | | `raw` | Tells parser that data is not processed and might need smoothing. Usually indicates that recorder didn't aply any filters when recording data from sensors. | 212 | | `user_adjusted` | Contents of the activity was adjusted or modified by user. Samples should not need any smoothing or filtering. | 213 | 214 | {% hint style="info" %} 215 | If `options` key is missing or is empty, parser must assume that data is presented in a `raw` state. 216 | {% endhint %} 217 | 218 | #### Accessibility Options 219 | 220 | Some activities can not be described completely using environment and sport alone. For such cases, `options` should provide more information. 221 | 222 | | Value | Description | 223 | | :--- | :--- | 224 | | `PTWCx` | Athlete uses a recumbent handcycle on the bike course and a racing wheelchair on the run segment \(in context of triathlon.\) | 225 | | `PTSx` | In both bike and run segments \(in context of triathlon,\) amputee athletes use prosthesis or other supportive devices. | 226 | | `PTVIx` | Athlete rides a tandem. | 227 | 228 | ### Sample 229 | 230 | The sequence of ordered samples is the foundation of successful data analysis. 231 | 232 | > Annotated example of a sample: 233 | 234 | ```javascript 235 | { 236 | "t": 1555678832, // datetime, unix epoch time 237 | "hr": 80, // heart rate, bpm 238 | "s": 1.4, // speed, m/s 239 | "l": { // location object 240 | "lt": 36.486331, // latitude 241 | "ln": 136.756827, // longitude 242 | "ha": 5 // horizontal accuracy, meters 243 | }, 244 | "p": 200, // power, watts 245 | "pb": 0.54, // power left-right ratio 246 | "el": 0.2, // elevation delta, meters 247 | "alt": 2314.2, // altitude, meters 248 | "grd": 2.1, // grade, % slope 249 | "at": 24.7, // temperature, celsius 250 | "d": 3.12, // displacement, meters 251 | "cd": 5001.4 // cumulative distance, meters 252 | "c": 80, // cadecne, rpm 253 | "crs": 98.34 // course, degrees 254 | } 255 | ``` 256 | 257 | | Key | Type | Unit | Description | 258 | | :--- | :--- | :---: | :--- | 259 | | `t` | [datetime]() | | MThe moment when sensors and data providers were sampled for the measurements. | 260 | | `hr` | int | BPM | Heart rate measurement | 261 | | `l` | [location]() | | Geographical 2D location of an athlete | 262 | | `p` | double | W | Total power measurement | 263 | | `pb` | double | | Power bias, or distributions between left and right limbs. Must be ignored if the sample has no `p` measurement. | 264 | | `c` | int | rpm[†]() | Cadence measurement | 265 | | `s` | double | m/s | Speed measurement | 266 | | `crs` | double | ºN | Course—the direction in which the athlete is traveling, measured in degrees and relative to due north. | 267 | | `d` | double | m | Displacement—the distance from the last sample that contains `d` key. | 268 | | `cd` | double | m | Cumulative distance—total distance covered from the start of the [activity](1.0.0-alpha.3.md#activity). | 269 | | `alt` | double | m | Absolute altitude of an athlete from sea level. | 270 | | `el` | double | m | Elevation—the altitude delta from the beginning of the activity. | 271 | | `vosc` | double | cm | Vertical oscillation measurement \(part of running dynamics.\) | 272 | | `gct` | double | ms | Ground contact time measurement—the time an athlete spends touching ground during the running stride \(part of running dynamics.\) | 273 | | `stpl` | double | m | Step length—the length of the single stride \(part of running dynamics.\) | 274 | | `grd` | double | % | Grade of the slope athlete is on. | 275 | | `at` | double | ºC | Ambient temperature measurement. | 276 | 277 | An empty sample is considered to be invalid and must be ignored. Non-empty sample must contain **`t` and at least one other measurement**. Sample containing the only `t` is also invalid and must be ignored. 278 | 279 | #### Location 280 | 281 | > Example of a location object with a latitude, longitude, horizontal accuracy of 30 centimeters and vertical accuracy of 5 meters: 282 | 283 | ```javascript 284 | { 285 | "lt": 35.679687, 286 | "ln": 139.757583, 287 | "ha": 0.3, 288 | "va": 5, 289 | } 290 | ``` 291 | 292 | Location object describes a geographical location on a 2D plane. Location object may include 2D or 3D precision of the location. Precision values may help data analysis software to calculate an athlete's relative path in a more precise manner. 293 | 294 | | Key | Type | Req. | Unit | Description | 295 | | :--- | :--- | :---: | :---: | :--- | 296 | | `lt` | double | ✓ | degrees[†]() | The latitude in degrees | 297 | | `ln` | double | ✓ | degrees[†]() | The longitude in degrees | 298 | | `ha` | double | | m | Horizontal accuracy | 299 | | `va` | double | | m | Vertical accuracy | 300 | 301 | It is recommended to store unprocessed location data from the sensors. Smoothing and other filtering techniques can be applied after data is decoded. 302 | 303 | #### Displacement and Cumulative Distance 304 | 305 | Unlike most fitness file formats, fitness·json supports two types of measurements to indicate athletes movement in terms of distance. TODO. 306 | 307 | When activity is completed outdoors with good GPS data, both _cumulative distance_ and _displacement_ can be recalculated, adjusted, or ignored. 308 | 309 | #### Latitude and Longitude Units 310 | 311 | Positive values indicate latitudes north of the equator. Negative values indicate latitudes south of the equator. 312 | 313 | #### Cadence Units 314 | 315 | Cadence unit depends on the [sport](). For simplicity's sake, it is generalized as RPM \(revolutions per minute\). However, it could mean SPM \(steps per minute\) for running, and PPM \(pushes per minute\) for a wheelchair. 316 | 317 | #### Power Bias 318 | 319 | Power bias \(value of `pb`\), or power distribution is a ratio of work between athlete's left and right limbs. Value of `0.0` means that the athlete's right limb is doing all of the work and value of `1.0` means that the athlete's left limb is doing all of the work. 320 | 321 | ```javascript 322 | { 323 | "t": 1555750866, 324 | "p": 234, 325 | "pb": 0.53 326 | } 327 | ``` 328 | 329 | For instance, imagine athlete producing 234 watts on a bicycle equipped with a dual-side power meter. Power bias value is 0.53, meaning athlete is producing 124W \(53% of the total power\) with the left leg and 110W \(47%\) with the right leg. 330 | 331 | A sample may not have power bias object while having value for `p`. Such a case, as an example, maybe be when the bicycle is equipped with a left-only power meter. 332 | 333 | ### Lap 334 | 335 | > Typical lap event triggered by an athlete: 336 | 337 | ```javascript 338 | { 339 | "t": "2019-04-20T04:20:42+00:00", 340 | "trigger": "manual" 341 | } 342 | ``` 343 | 344 | Lap object describes an event. Lap events can be triggered manually by an athlete, or automatically—by fitness equipment or the [guide](). 345 | 346 | | Key | Type | Req. | Description | 347 | | :--- | :--- | :---: | :--- | 348 | | `t` | datetime | ✓ | Datetime of the event triggered | 349 | | `trigger` | [lap\_trigger]() | | DSescribes what or who triggred an event. | 350 | 351 | #### Lap Trigger 352 | 353 | Lap trigger describes means by which lap event was initiated. 354 | 355 | | Value | Description | 356 | | :--- | :--- | 357 | | `unknown` | **Default** value. Asumed when no `trigger` is present in lap object. | 358 | | `manual` | Athelete pushed _Lap_ button. | 359 | | `distance` | Distance based auto-lap feature of an app. | 360 | | `timer` | Time based auto-lap feature of an app. | 361 | | `posision` | Geographical position or fence. | 362 | | `equipment` | Fitness equpement requeested start of new lap. | 363 | | `guide` | [Guide]() procedded to next interval or block. | 364 | 365 | ### Marker 366 | 367 | > Simple marker noting a particular moment: 368 | 369 | ```javascript 370 | { 371 | "t":"2019-04-20T04:20:42+00:00" 372 | } 373 | ``` 374 | 375 | > Marker with a note: 376 | 377 | ```javascript 378 | { 379 | "t":"2019-04-20T04:20:42+00:00", 380 | "note": "Feels like I've hit the wall." 381 | } 382 | ``` 383 | 384 | Marker object describes a specific point in time of the activity for the later reference. Marker object `t` value can be mapped to the corresponding sample and its measurements by the data analysis software. 385 | 386 | | Key | Type | Req. | Description | 387 | | :--- | :--- | :---: | :--- | 388 | | `t` | datetime | ✓ | Mokent of time to mark. | 389 | | `trigger` | string | | Short note. | 390 | 391 | ## Guide 392 | 393 | Guide, or Guided Workout, is an object or file with a set of instructions of how the athlete should perform the activity. Guides are great tools for structured workouts as they can be as loose or as strict as the author designs them to be. 394 | 395 | ### Anatomy of Guide 396 | 397 | There are couple of object object that make up a single guide. 398 | 399 | 👉 TODO: Visualisation of the guide, its intervals, and blocks. 400 | 401 | Guides consist of [intervals](), [blocks]() are groups of blocks, and blocks are atomic objects that describe a single step of work in the guide. The guide can be visualized as a queue of blocks. Intervals unify some blocks into groups and tells the guide what environment, sport and repeat count they share. 402 | 403 | #### Top Level 404 | 405 | ```text 406 | { 407 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#gudie", 408 | "title": "Tempo Ride", 409 | "symbol": "T1", 410 | "symbol_color": "#FF9900", 411 | "intervals": [ 412 | { 413 | // ... 414 | } 415 | ], 416 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 417 | "author": { 418 | "name": "John Appleseed", 419 | "email": "johnappleseed@example.com", 420 | "link": "https://leopardskins-cyclingclub.org" 421 | }, 422 | "created_at": "2019-04-21T00:41:57+00:00", 423 | "ref": "https://leopardskins-cyclingclub.org/guides/" 424 | } 425 | ``` 426 | 427 | Non-empty guides must have at least one interval with at least one block. Otherwise, the guide is considered to be empty. The empty guide is not suitable for workout and should be ignored or edited. 428 | 429 | | Key | Type | Req. | Description | 430 | | :--- | :--- | :---: | :--- | 431 | | `version` | string | ✓ | URL of the version of the format the object and/or file. | 432 | | `title` | string | | Title of the guide. | 433 | | `symbol` | string | | Abbreviation or shorthand code for easy guide identification. No more than two characters. | 434 | | `symbol_color` | string | | Color of the symbol for easy guide identification. A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 435 | | `description` | string | | A relatively long-form description of the guide. An excellent place to inform the athlete what this guide is trying to achieve, what to expect during the workout and any other information that may be useful for the athlete before she starts the workout. | 436 | | `intervals` | [\[interval\]]() | ✓ | An array of ordered intervals. | 437 | | `author` | [author]() | | Name and contact information of the author of this guide. | 438 | | `created_at` | datetime | ✓ | The date and time of guide creation. | 439 | | `ref` | string | | Reference URL pointing to any auxiliary or related information. | 440 | 441 | ### Interval 442 | 443 | > Example of segnment with two blocks that will be repeated twice in the workout. 444 | 445 | ```text 446 | { 447 | "title": "Max Power", 448 | "env": "outdoor", 449 | "sport": "run", 450 | "repeats": 2, 451 | "blocks": [ 452 | { 453 | // ... 454 | }, 455 | { 456 | // ... 457 | } 458 | ], 459 | "description": "Maximum effort followed by 2 min rest. Two sets. Go!" 460 | } 461 | ``` 462 | 463 | Interval objects provide structure to the guide. They group related blocks, as well as tell the guide what environment and sport those blocks should be performed. Intervals also can have repeat count, thus eliminating the need to have duplicate groups of blocks in sequence. 464 | 465 | | Key | Type | Req. | Description | 466 | | :--- | :--- | :---: | :--- | 467 | | `repeats` | int \[1..∞\)[†]() | | The number of repeats. Describes how many times a set of blocks should be repeated during a workout. | 468 | | `title` | string | | Title of the interval. | 469 | | `sport` | [sport]() | ✓ | Sport. | 470 | | `env` | [environment]() | ✓ | Environment. | 471 | | `blocks` | [\[block\]]() | ✓ | An array of ordered blocks that are part of this interval. | 472 | | `description` | string | | Short instruction and overview of the block for the athlete. | 473 | 474 | #### Repeats 475 | 476 | When `repeat` has a value greater than `1`, the guide should put the interval's first block in the queue right after its last block. Only after blocks in the interval has been performed `repeat` times, the guide must advance to the next interval \(first block of the next interval\). This process should repeat until the guide has run out of blocks and their parent intervals. 477 | 478 | `repeat` is optional, and when absent from the object, the decoder should assume the value of `1`. Values less than `1` are invalid. 479 | 480 | ### Block 481 | 482 | ```javascript 483 | { 484 | "type" : "work", 485 | "duration" : { 486 | "condition" : "gte", 487 | "trigger" : "distance", 488 | "value" : 5000 489 | }, 490 | "target_type" : "rvalue", 491 | "target_rvalue" : { 492 | "anchor" : "threshold", 493 | "lower" : 0.95, 494 | "upper" : 1.05, 495 | "metric" : "heart_rate" 496 | }, 497 | "description" : "Run for 5K. Try to be near your LTHR.", 498 | } 499 | ``` 500 | 501 | Block object, or work block, describes a single unit of work in the guide. Block can be one of [several types](), have completion condition, and optionally, target. Targets itself, can be [one of four types](). 502 | 503 | | Key | Type | Req. | Description | 504 | | :--- | :--- | :---: | :--- | 505 | | `type` | [type]() | ✓ | Type of the block. Describes the kind of work to perform. | 506 | | `duration` | [duration]() | ✓ | The condition upon meeting which block is complete and guide advances to the next block. | 507 | | `target_type` | [target\_type]() | ✓ | Type of the target. | 508 | | `target_value` | [value\_target]() | | The object describing target when `target_type` is `value`. | 509 | | `target_rvalue` | [rvalue\_target]() | | The object describing target when `target_type` is `rvalue`. | 510 | | `target_zone` | [zone\_target]() | | The object describing target when `target_type` is `zone`. | 511 | | `description` | string | | Short instruction and overview of the block. | 512 | 513 | #### Block Type 514 | 515 | Block type object describes the nature of work in the current block. Those are provided mostly for visual reference. Type of work does not affect the block's completion duration, nor on target. 516 | 517 | | Value | Description | 518 | | :--- | :--- | 519 | | `warmup` | Warm-up. | 520 | | `work` | Relatively hi-intensity activity. | 521 | | `rest` | Lower intensity activity. | 522 | | `cooldown` | Cooldown. | 523 | 524 | ### Block Completion 525 | 526 | The guide can advance by itself or manually. Block completion object describes exact instruction on how the block should advance to the next block in the guide. 527 | 528 | | Key | Type | Req. | Description | 529 | | :--- | :--- | :---: | :--- | 530 | | `trigger` | [trigger]() | ✓ | What triggers advancement to the next block. | 531 | | `condition` | [condition]() | | What conditions should be satisfied by the trigger. | 532 | | `value` | double | | Value of measurement based on which condition is applied. | 533 | 534 | #### Trigger 535 | 536 | > Example of a cooldown block that ends when the athlete's heart rate drops below 60 BPM: 537 | 538 | ```javascript 539 | { 540 | "duration": { 541 | "condition": "lt", 542 | "trigger": "heart_rate", 543 | "value": 60 544 | }, 545 | "description": "Run for 5K", 546 | "type": "cooldown", 547 | "target_type": "none" 548 | } 549 | ``` 550 | 551 | Event or measurement that triggers the advancement to the next block or guide completion. When the trigger is set to `user`, the guide will not advance automatically. It is good practice to remind athletes about that using the block's description object. Athlete can manually advance to the next block irrespective what trigger value is. 552 | 553 | | Value | Description | 554 | | :--- | :--- | 555 | | `user` | Athlete requests next block. | 556 | | `time` | Block timer. | 557 | | `distance` | Block distance. | 558 | | `hr` | Live heart rate. | 559 | | `block_hr` | Block avarage heart rate. | 560 | | `speed` | Live speed or pace. | 561 | | `block_speed` | Block avarage speed or pace. | 562 | | `cadence` | Live cadence. | 563 | | `power` | Live power. | 564 | | `power3` | Live power avarege of last 3 seconds. | 565 | | `power10` | Live power avarege of last 10 seconds. | 566 | | `power30` | Live power avarege of last 30 seconds. | 567 | | `block_power` | Block avarage power. | 568 | | `swim_stroke` | Swim stroke count. | 569 | 570 | #### Condition 571 | 572 | > Example of the block that advances to the next block after 5K: 573 | 574 | ```text 575 | { 576 | "duration": { 577 | "condition": "gte", // note `gte` instead of `eq` 578 | "trigger": "distance", 579 | "value": 5000 580 | }, 581 | "description": "Run for 5K", 582 | "type": "work", 583 | "target_type": "none" 584 | } 585 | ``` 586 | 587 | > You could read out loud above block's completion condition like so: _“This block will be done when athlete covers the distance that is greater than or equal to 5000 meters.”_ 588 | 589 | Condition object describes an operator that is applied to the measurement and value of value object in the block completion. 590 | 591 | | Value | Symb. | Description | 592 | | :--- | :---: | :--- | 593 | | `lt` | < | Less than | 594 | | `lte` | ≤ | Less than or equal to. | 595 | | `eq` | = | Strictly equal to. | 596 | | `gte` | ≥ | Greater than or equal to. | 597 | | `gt` | > | Greater than. | 598 | 599 | Note that equality \(`eq`\) is strict equality. When designing blocks that are complete at after 5000 meters, guide designer must use greater than or equal to condition operator instead of equal to as recorder might sample distance values just before and after athlete passes 5K mark. Neither 4998.9 not 5000.4 are strictly equal to 5000, thus block never ends. 600 | 601 | ### Block Targets 602 | 603 | > Example block without a target has a duration of two minutes: 604 | 605 | ```javascript 606 | { 607 | "type": "work", 608 | "target_type": "none", 609 | "duration": { 610 | "condition": "gte", 611 | "trigger": "time", 612 | "value": 120 613 | }, 614 | "description": "Simply run for two minutes.", 615 | } 616 | ``` 617 | 618 | Block object can have one of four types of targets as described in `target_type`. 619 | 620 | | Value | object Key | Description | 621 | | :--- | :--- | :--- | 622 | | [`none`]() | N/A | Block has no target \(a.k.a. _Free Block_.\) | 623 | | [`value`]() | `target_value` | The athlete is asked to be in the range of specific numbers for the given [metric](). | 624 | | [`rvalue`]() | `target_rvalue` | The athlete is asked to be the range that is calculated by multiplying relative values to fitness values from the [profile]() of the given [metric]() and [anchor](). | 625 | | [`zone`]() | `target_zone` | The athlete is asked to hit zone at the given index. Zone index is independent of the athlete's choice zone set, The guide should specify what zone-set it is designed to work. | 626 | 627 | Unless `none` is a value of `target_type`, the block must have a corresponding target object as its content. 628 | 629 | #### No Target \(Free Block\) 630 | 631 | No target block. The athlete may do whatever they want. This kind of block is useful when "target" is to complete the block. 632 | 633 | #### Absolute Value Target 634 | 635 | > Example of block with `target_type: value`. An athlete is asked to maintain heart rate within 80–100 BPM range for 20 minutes: 636 | 637 | ```javascript 638 | { 639 | "type": "work", 640 | "target_type": "value", 641 | "target_value": { 642 | "metric": "heart_rate", 643 | "lower": 80, 644 | "upper": 100, 645 | }, 646 | "duration": { "condition": "gte", "trigger": "time", "value": 1200 } 647 | } 648 | ``` 649 | 650 | Absolute value target object describes the specific range of values of a given [metric]() that athlete should aim. 651 | 652 | All fields are required. 653 | 654 | | Key | Type | Description | 655 | | :--- | :--- | :--- | 656 | | `metric` | [metric]() | Target metric | 657 | | `lower` | double | Lower bound | 658 | | `upper` | double | Upper bound \(exclusive\) | 659 | 660 | While absolute value targets are straightforward and easy to understand, note that it is infrequent when the same values work for different athletes. If you intent sharing the guide online, consider using [Relative Value Target]() objects as they calculate absolute ranges based on an athlete's profile. 661 | 662 | #### Relative Value Target 663 | 664 | > Example of the block with `target_type: rvalue`. An athlete is asked to maintain 90–110% of her FTP during 2 minutes: 665 | 666 | ```javascript 667 | { 668 | "type": "work", 669 | "target_type": "rvalue", 670 | "target_value": { 671 | "metric": "power", 672 | "anchor": "threshold", 673 | "lower": 0.9, 674 | "upper": 1.1, 675 | }, 676 | "duration": { "condition": "gte", "trigger": "time", "value": 120 } 677 | } 678 | ``` 679 | 680 | Unlike absolute value target, relative values targets are calculated based on data points from the athlete's [profile](). They can be universal for many types among the people and automatically change over time as an athlete's physique and profile changes. As with absolute values, the target range is set with `lower` and `upper` bound values, but instead of exact measurements, they contain multipliers. Those values get multiplied by a measurement from profile based on the combination of values from `metric` and `anchor`. 681 | 682 | For instance, if the value of `metric` is `power` and value of `anchor` is `threshold`, then multipliers from the target's `lower` and `upper` are applied to the [profile]()'s `ftp` value. 683 | 684 | All fields are required. 685 | 686 | | Key | Type | Description | 687 | | :--- | :--- | :--- | 688 | | `metric` | [metric]() | Target metric | 689 | | `anchor` | [anchor]() | Relative to what profile measurement | 690 | | `lower` | double | Lower bound multiplier | 691 | | `upper` | double | Upper bound multiplier \(exclusive\) | 692 | 693 | #### Zone Target 694 | 695 | > Example of the block with `target_type: zone`. An athlete is asked to maintain heart rate zone 3 for 4 kilometers: 696 | 697 | ```javascript 698 | { 699 | "type": "work", 700 | "target_type": "zone", 701 | "target_value": { 702 | "metric": "heart_rate", 703 | "index": 3, 704 | }, 705 | "duration": { "condition": "gte", "trigger": "distance", "value": 4000 } 706 | } 707 | ``` 708 | 709 | Zone Target object describes which zone athlete should aim for, and what kind of zone it should be. Note that the value of `index` is an index of a zone in an array of zones in a zone set. Meaning, if an athlete replaces zone set with a different one, a new zone that is located at the same index of `index` becomes the new target zone. It is an athlete's responsibility to make sure that selected zone set matches requirements of the target and guide. 710 | 711 | All fields are required. 712 | 713 | | Key | Type | Description | 714 | | :--- | :--- | :--- | 715 | | `metric` | [metric]() | Type of metric of which zone should be used. | 716 | | `index` | int | Index of a selected zone-set. | 717 | 718 | #### Metric 719 | 720 | Metric object describes what type of measurement target value range is. 721 | 722 | | Value | Description | 723 | | :--- | :--- | 724 | | `heart_rate` | Heart rate | 725 | | `cadence` | Cadence — RPM for cycling and handcycling; SPM for running; PPM for wheelchair. | 726 | | `power` | Power. | 727 | | `speed` | Speed or pace. | 728 | 729 | #### Metric Anchor 730 | 731 | Anchor object describes relative to which data point in athlete's [profile]() the final target range should be calculated against. 732 | 733 | | Value | Description | 734 | | :--- | :--- | 735 | | `min` | An athlete's minumum measured value for the metric \(e.g. **Resting HR** when `metric: heart_rate`.\) | 736 | | `max` | An athlete's maximum measured value for the metric \(e.g. **MaxHR** when `metric: heart_rate`.\) | 737 | | `threshold` | An athlete's threashold value for the metric \(e.g. **FTP** when `metric: power` and `sport: cycle`.\) | 738 | | `test` | Test time measurement \(e.g. swim test time\) | 739 | 740 | There are many combinations of `metric` and `anchor` that do not make sense. Such cases should be handled with care. For instance, what is minimum power? Minimum power athlete can produce is always equal to 0 watts \(i.e., not pedaling.\) Anchoring a target range on 0 watts is not particularly useful since both, lower and upper bounds always equal to 0. 741 | 742 | ## Profile 743 | 744 | > Example of an athelete's complete profile: 745 | 746 | ```javascript 747 | { 748 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#profile", 749 | "name": "John Appleseed", 750 | "email": "johnappleseed@example.com", 751 | "hrr": 48, 752 | "hrmax": 201, 753 | "lthr": 169, 754 | "ftp": 220, 755 | "rftpw": 240, 756 | "vo2max": 39, 757 | "weight": 74.4, 758 | "height": 170, 759 | "swim_test_dist": 1000, 760 | "swim_test_time": 500, 761 | "run_tpace": 4.7, 762 | "birthday": "1988-04-17", 763 | "sex": "male", 764 | "wheelchair": false, 765 | "handcycle": false 766 | } 767 | ``` 768 | 769 | > Example of the minimal profile for a cyclist: 770 | 771 | ```javascript 772 | { 773 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2.0#profile", 774 | "lthr": 169, 775 | "ftp": 220, 776 | "vo2max": 39, 777 | "weight": 74.4, 778 | } 779 | ``` 780 | 781 | Profile object describes various athlete's measurements and data points. Profile object features several arbitrary categories: basic body measurements, fitness test results, accessibility information, and contact information. 782 | 783 | Profile object is not a database; hence there is no historical information in it. When faced with a profile without any context, the decoder should assume that profile has _latest known data_. When profile object is part of another object \([for instance, a workout]()\), then the decoder can map the parent object's date to the profile. 784 | 785 | #### Top Level 786 | 787 | Since profile may be embedded into other objects, all values are optional. 788 | 789 | | Key | Type | Unit | Description | 790 | | :--- | :--- | :--- | :--- | 791 | | `version` | url | | URL of the version of the format the object. | 792 | | `name` | string | | Full name of an athlete. | 793 | | `email` | string | | Contact email of an athlete. | 794 | | `birthday` | [datetime]() | | Date of birth. | 795 | | `sex` | [sex]() | | Biological sex of an athelete. | 796 | | `hrr` | int | BPM | Resting heart rate. | 797 | | `hrmax` | int | BPM | Maximum heart rate. | 798 | | `lthr` | int | BPM | Lactate threashold heart rate, LTHR. | 799 | | `ftp` | int | W | Cycling threashold power, FTP. | 800 | | `rftpw` | int | W | Runnning functional threashold power, rFTPw. | 801 | | `vo2max` | int | mL/\(kg·min\) | Maximal oxygen consumption of an athlete. | 802 | | `weight` | int | kg | Weight of an athelete. | 803 | | `swim_test_dist` | double | m | Distance of the swim test. | 804 | | `swim_test_time` | double | s | Recorded completion time of the swim test \(for the distance.\) | 805 | | `run_tpace` | double | m/s | Running threashold pace. | 806 | | `wheelchair` | bool | | `true` when atehlete is a wheelchair user. Ommition of this key is equvalentn of `false` value. | 807 | | `handcycle` | bool | | `true` when atehlete is a handlcuyle user. Ommition of this key is equvalentn of `false` value. | 808 | 809 | `swim_test_dist` and `swim_test_time` are only interdependent values in the profile object. They are considered as valid only if both are present. 810 | 811 | Developers should pay extra attention when sharing profile to other parties or apps, as it may contain highly private information, both from user privacy, and competitive perspective. **Do not share a profile object in any form without user consent.** 812 | 813 | ## Route 814 | 815 | > Example of a route with single POI and two paths: 816 | 817 | ```javascript 818 | { 819 | "version": "https://fitnessjson.org/version/1.0.0-alpha.2#route", 820 | "name": "Sunday Group Ride (Spring 2019)", 821 | "description": "Note for new members: we'll take alternative path starting from 30km point (marked so) to avoid two climbs.", 822 | "paths": [ 823 | { 824 | "waypoints": [ 825 | { "lt": 36.486331, "ln": 136.756827 }, 826 | { "lt": 36.486331, "ln": 136.756827 }, 827 | { "lt": 36.486331, "ln": 136.756827 }, 828 | ], 829 | "name": "Main Cycling Path", 830 | "color": "#F7C948" 831 | }, 832 | { 833 | "waypoints": [ 834 | { "lt": 36.486331, "ln": 136.756827 }, 835 | { "lt": 36.486331, "ln": 136.756827 }, 836 | { "lt": 36.486331, "ln": 136.756827 }, 837 | ], 838 | "name": "Novice Detour", 839 | "color": "#8DED2D" 840 | } 841 | ], 842 | "poi": [ 843 | { 844 | "coordinate": { "lt": 36.486331, "ln": 136.756827 }, 845 | "name": "Coffee Stop", 846 | "color": "#E67635" 847 | } 848 | ], 849 | "ref": "https://leopardskins-cyclingclub.org/ride/1234" 850 | } 851 | ``` 852 | 853 | Routes are geographical information about paths and POIs \(points-of-interest\) used as a map overlay while performing an activity. Routes shouldn't require specific workouts but may have additional information. The primary use of the route is to serve as additional geographical information for an athlete regardless of activity or sport. Example paths could be weekly sunday bike ride route with its alternative loops, and resting and coffee-stop locations. 854 | 855 | The athlete may choose to use routes as an assistance to a guided workout and have POIs indicating starts or ends of significant efforts or target heart rate zones. 856 | 857 | #### Top Level 858 | 859 | Route files are self-contained and not part of any other file or object. 860 | 861 | | Key | Type | Req. | Description | 862 | | :--- | :--- | :---: | :--- | 863 | | `version` | url | ✓ | URL of the version of the format the file. | 864 | | `name` | string | | Title of the route file. | 865 | | `description` | string | | Descriptive, multiline text. | 866 | | `paths` | [\[path\]]() | | Array of paths that are part of the route. | 867 | | `pois` | [\[path\]]() | | Colleciton of points of interest. | 868 | | `ref` | string | | Reference URL. | 869 | 870 | ### Path 871 | 872 | The path is a representation of a single, uninterrupted track within a route, presented as a colored line with a short name that describes it. 873 | 874 | > Example of a short path that is made of six waypoints: 875 | 876 | ```javascript 877 | { 878 | "waypoints": [ 879 | { "lt": 36.486331, "ln": 136.756827 }, 880 | { "lt": 36.486331, "ln": 136.756827 }, 881 | { "lt": 36.486331, "ln": 136.756827 }, 882 | { "lt": 36.486331, "ln": 136.756827 }, 883 | { "lt": 36.486331, "ln": 136.756827 }, 884 | { "lt": 36.486331, "ln": 136.756827 } 885 | ], 886 | "name": "Main Cycling Path", 887 | "color": "#F7C948" 888 | } 889 | ``` 890 | 891 | | Key | Type | Req. | Description | 892 | | :--- | :--- | :---: | :--- | 893 | | `waypoints` | [\[location\]]() | ✓ | An array of ordered coordinates that make up the path. | 894 | | `name` | string | | Short title. | 895 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 896 | 897 | Order of coordinates in `waypoints` is essential. The path is made by connecting previous waypoint to the current one. After that cursor is advanced to the next waypoint and process is repeated until the waypoints array is exhausted. 898 | 899 | Each path is uninterrupted. User can use multiple paths with the same `name` and `color` if such behavior is required. 900 | 901 | Note that horizontal and vertical accuracy values \(`ha` and `va` respectively\) in location, when used describing path will be ignored. Path objects must have accurate coordinates. 902 | 903 | ### POI 904 | 905 | POI, or Point of Interest, is a representation of a single place on the route, presented as a color dot with a short name that describes it. POIs are designed to be glanceable and straightforward during the activity. 906 | 907 | > Example of a café near Kanazawa: 908 | 909 | ```javascript 910 | { 911 | "coordinate": { 912 | "lt": 36.486331, 913 | "ln": 136.756827 914 | }, 915 | "name": "1st Coffee Stop", 916 | "color": "#E67635" 917 | } 918 | ``` 919 | 920 | | Key | Type | Req. | Description | 921 | | :--- | :--- | :---: | :--- | 922 | | `coordinate` | [location]() | ✓ | Coordinate of the place. | 923 | | `name` | string | | Short title. | 924 | | `color` | string | | A 16-bit, hexadecimal representation of the color. e.g. `#FF9900` for the orange. May not contain leading `#`. | 925 | 926 | Note that horizontal and vertical accuracy values \(`ha` and `va` respectively\) in location, when used describing POI will be ignored. POI objects must have accurate coordinates. 927 | 928 | ## Common Types 929 | 930 | ### DateTime 931 | 932 | DateTime, date, or just `t`, should be specified as a string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format or [UNIX Time](https://en.wikipedia.org/wiki/Unix_time). 933 | 934 | In cases where DateTime repeats frequently, or sub-second precision is required, the encoder should use UNIX time format. It is a good practice to use ISO 8601 in all cases but in [samples](), where **UNIX Time** is advantageous as the value of `t`. 935 | 936 | ### Sport 937 | 938 | Following sports are suppoted by fitness·json: 939 | 940 | | Value | Description | 941 | | :--- | :--- | 942 | | `run` | Runnning | 943 | | `cycle` | Cycling on a two-wheeled bicycle | 944 | | `swim` | Any style of swimming | 945 | | `walk` | Walking. Maybe a slow-paced stroll, tracking, or racewalking. | 946 | | `transition` | Activity performed in between two different disciplines, as in triathlon. Should be treated synonymous to running when no accessibility options are present. | 947 | | `wheelchair` | Race pace wheelchair | 948 | | `handcycle` | Handcycling | 949 | 950 | Note that `wheelchair` and `handcycle` are distinct types of activity and must not be replaced by `run` and `cycle` respectively. fitness·json format respects para-sport as-is and treats them as first-class citizens. 951 | 952 | ### Environment 953 | 954 | The environment of activity. 955 | 956 | | Value | Description | 957 | | :--- | :--- | 958 | | `indoor` | Performed indoors. Usually means that GPS data is not available or should be ignored. | 959 | | `outdoor` | Performed outdoors, where GPS information and atmospheric conditions matter or provide context. | 960 | | `virtual` | Performed in vistual environments such as computer games, simulations, etc. | 961 | 962 | ### Sex 963 | 964 | Biological sex may be useful when calculating calorie burn during the workouts. Ideally, this metric should be highly optional and not required Possible values for sex are: 965 | 966 | | Value | Description | 967 | | :--- | :--- | 968 | | `unknown` | **Default** value | 969 | | `male` | Male | 970 | | `female` | Female | 971 | | `other` | Athlete explicitly declined to provide sex information. | 972 | 973 | **Difference beween `unknown` and `other`** 974 | 975 | Value of `unknown` is assumed as the default value when the athlete does not provide sex. This value of `unknown` grants application to make an educated guess of the athlete's biological sex if it is beneficial to the data analysis. Value of `other`, explicitly tells the decoder that sex-based decisions must be avoided as they are inaccurate or not appropriate for the athlete. 976 | 977 | ### Author 978 | 979 | > Example of complete author object: 980 | 981 | ```javascript 982 | { 983 | "name": "Joana Appleseed", 984 | "email": "joanaappleseed@example.com", 985 | "link": "https://leopardskins-cyclingclub.org", 986 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 987 | } 988 | ``` 989 | 990 | Auhtor, or person object describes and indetifies a person and her contact information. 991 | 992 | | Key | Type | Req. | Description | 993 | | :--- | :--- | :---: | :--- | 994 | | `name` | string | | Full name of a person. | 995 | | `email` | string | | Contact email of a person. | 996 | | `link` | string | | Home web page, blog, or social account. | 997 | | `profile_picture_url` | string | | URL string of the profile image. Should not be a large file. | 998 | 999 | ### Comment 1000 | 1001 | > Example of comment object: 1002 | 1003 | ```javascript 1004 | { 1005 | "date": "2019-04-21T04:37:13+00:00", 1006 | "text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 1007 | "author": { 1008 | "name": "Joana Appleseed", 1009 | "email": "joanaappleseed@example.com", 1010 | "link": "https://leopardskins-cyclingclub.org", 1011 | "profile_picture_url": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" 1012 | } 1013 | } 1014 | ``` 1015 | 1016 | Auhtor, or person object describes and indetifies a person and her contact information. 1017 | 1018 | | Key | Type | Req. | Description | 1019 | | :--- | :--- | :---: | :--- | 1020 | | `date` | datetime | ✓ | Date comment was created | 1021 | | `text` | string | ✓ | Contents of the comment | 1022 | | `author` | [author]() | | Author of the comment. | 1023 | 1024 | When `author` is not present in comment object, the decoder should assume the athlete herself authored a comment as a post-workout note. 1025 | 1026 | --------------------------------------------------------------------------------