├── 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 |
--------------------------------------------------------------------------------