{
    "componentChunkName": "component---src-components-page-template-jsx",
    "path": "/guides/main/data-recording-playback",
    "result": {"data":{"mdx":{"id":"00e1e61b-e337-5552-bee5-80a05c5555b3","body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"section\": \"Guides\",\n  \"chapter\": \"Main Codebase\",\n  \"title\": \"Data Recording and Playback\",\n  \"description\": \"How to record data from a running system and play it back.\",\n  \"slug\": \"/guides/main/data-recording-playback\",\n  \"authors\": [\"Ysobel Sims (@ysims)\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"The NUbots system utilises \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/system/foundations/nuclear\"\n  }, \"NUClear's message passing architecture\"), \". Any \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/NUbots/NUbots/tree/main/shared/message\"\n  }, \"messages\"), \" that are emitted when the system is running can be recorded and saved as \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/system/foundations/nuclear#nbs-files\"\n  }, \"NUClear Binary Stream (NBS) files\"), \". These NBS files can later be filtered, played back (to simulate the running system), analysed, and converted to alternative formats for further processing.\"), mdx(\"h2\", {\n    \"id\": \"recording-data\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#recording-data\",\n    \"aria-label\": \"recording data permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Recording Data\"), mdx(\"p\", null, \"The following steps detail how to record data. An example will be given throughout showing how to record sensors messages on a real robot.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Set up the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/system/foundations/nuclear#nuclear-roles\"\n  }, \"role\"), \" for recording. Most of the time this involves making minor changes to an existing role.\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The role must contain the module/s which emit the message/s you want to record.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The role must contain the module \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"support::logging::DataLogging\"), \".\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"For example, you may want to record \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/NUbots/NUbots/blob/main/shared/message/input/Sensors.proto\"\n  }, \"Sensors\"), \" messages. You could use the existing \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/NUbots/NUbots/blob/main/roles/test/sensor.role\"\n  }, \"test/sensor\"), \" role and add \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"support::logging::DataLogging\"), \" to it.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Set up the data logging configuration file. This involves making changes to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/NUbots/NUbots/tree/main/module/support/logging/DataLogging/data/config\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"DataLogging.yaml\")), \".\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add in the messages you want to log, and set their values to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \".\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"In this example, the sensors message already exists in the file (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"message.input.Sensors\"), \"). Set the boolean to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \" next to this message.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If you would like to log a different message and it doesn't already have a corresponding line in the DataLogging configuration file, do the following:\"), mdx(\"ol\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Find the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".proto\"), \" file that defines that message, such as \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://github.com/NUbots/NUbots/blob/main/shared/message/input/Sensors.proto\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"Sensors.proto\")), \".\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Find what the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"package\"), \" is. For \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Sensor.proto\"), \", it is \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"package message.input\"), \".\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Piece this together with the message name (\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"message Sensors\"), \" in the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".proto\"), \" file) to get \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"message.input.Sensors\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add this to the configuration file with the other messages.\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You can enable multiple messages at once, and they will all be recorded.\")))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Build the code and install it to a robot. To find out how to do this, visit the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/guides/main/getting-started\"\n  }, \"Getting Started\"), \" page.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Run the binary for the role you want to record.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Locally in Docker:\"), \" In the same location you built the code, run the binary using the command \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./b run <role_name>\"), \".\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"On a real robot:\"), \" \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://en.wikipedia.org/wiki/Secure_Shell_Protocol\"\n  }, \"SSH\"), \" into the robot using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ssh nubots@<address>\"), \" and run the binary built for the role you want to record. For our example, run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./test/sensor\"), \".\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Stop the binary when you have finished recording by pressing \", mdx(\"kbd\", null, \"Ctrl\"), \"+\", mdx(\"kbd\", null, \"C\"), \".\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Retrieve the log file.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Locally in Docker:\"), \" The log file will be in your NUbots directory under \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/recordings/<role_name>\"), \".\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"On a real robot:\"), \" You will need to move the file off the robot so that it is not lost and so that it can be used. Note that when someone else uses the robot any recordings may be lost, so immediately copy them to another computer.\"), mdx(\"ol\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"The NBS file will be located on the robot in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"recordings/role_name/\"), \" folder. The name will correspond to the time and date it was created, according to the robot's clock. Make note of the name. In our example, this may be \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"recordings/test/sensor/18072020T082100.nbs\"), \".\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Copy the file across. On the destination computer, run:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"scp nubots@<address>:recordings/rolename/file_name path/to/destination\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"For our example, you could run:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Replace <address> with the robot's IP address\\nscp nubots@<address>:recordings/test/sensor/18072020T082100.nbs .\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"The file will be copied from the robot to the folder you ran the command in. The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://linux.die.net/man/1/scp\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"scp\")), \" command is like the regular \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://linux.die.net/man/1/cp\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"cp\")), \" command, but it copies to or from another computer over SSH. The first path in the command points to the file we are copying and the second path points to where we want to copy that file to.\"))))), mdx(\"h2\", {\n    \"id\": \"extracting-data\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#extracting-data\",\n    \"aria-label\": \"extracting data permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Extracting Data\"), mdx(\"p\", null, \"NBS files are binary and not easily human-readable. There are tools in the NUbots codebase to convert NBS files to alternative formats that are easier to understand.\"), mdx(\"p\", null, \"These tools are grouped under the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/NUbots/NUbots/tree/main/tools/nbs\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"./b nbs\")), \" command in the NUbots codebase, and can be executed by running:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b nbs <tool_name> <nbs_file_name> <arguments>\\n\")), mdx(\"h3\", {\n    \"id\": \"stats\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#stats\",\n    \"aria-label\": \"stats permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Stats\"), mdx(\"p\", null, \"Generates statistics about the messages in an NBS file. This includes a count of each message type in the file, and how many messages there are in total.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Example\\n./b nbs stats path/to/file.nbs\\n\")), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Argument\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Definition\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--message-timestamp\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Uses the message's \", mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \".timestamp\"), \" field as the time of the message.\")))), mdx(\"h3\", {\n    \"id\": \"json\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#json\",\n    \"aria-label\": \"json permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"JSON\"), mdx(\"p\", null, \"Converts the data in an NBS file to JSON, and prints the output. To save the output to a file, append \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"> file.json\"), \" to the command.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Example\\n./b nbs json path/to/file.nbs > file.json\\n\")), mdx(\"h3\", {\n    \"id\": \"extract-images\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#extract-images\",\n    \"aria-label\": \"extract images permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Extract Images\"), mdx(\"p\", null, \"Extracts JPEG image files from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CompressedImage\"), \" messages in an NBS file. It will also write each image's metadata (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Hcw\"), \" and lens information) to a JSON file next to the extracted image.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Example\\n./b nbs extract_images path/to/file.nbs --output path/to/output-folder/\\n\")), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Argument\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Definition\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Default\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--output\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Specifies the folder to save the images to.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The current working directory\")))), mdx(\"h3\", {\n    \"id\": \"video\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#video\",\n    \"aria-label\": \"video permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Video\"), mdx(\"p\", null, \"Extracts images from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CompressedImage\"), \" messages in an NBS file, and combines them into an MP4 video.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Example\\n./b nbs video path/to/file.nbs --output path/to/output-folder/\\n\")), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Argument\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Definition\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Default\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--output\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The folder to save the video to.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The current working directory\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--quality\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The quality to save the video in.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"30M\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--encoder\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The encoder to use when encoding the video.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"h264_nvenc\")))), mdx(\"h3\", {\n    \"id\": \"calibrate-cameras\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#calibrate-cameras\",\n    \"aria-label\": \"calibrate cameras permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Calibrate Cameras\"), mdx(\"p\", null, \"Calculates camera intrinsics using an NBS recording of asymmetric circles grid patterns.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Argument\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Definition\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Default\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--config\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The path to the directory containing the camera configuration files.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Required\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--rows\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The number of rows in the asymmetric circles grid.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--cols\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The number of columns in the asymmetric circles grid.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"11\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--grid_size\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"The distance between rows/cols in the grid in meters.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"0.04\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--no-intrisics\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Does not change the intrinsic configuration values.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"-\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"--no-extrinsics\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Does not change the extrinsic configuration values.\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"-\")))), mdx(\"h3\", {\n    \"id\": \"foot-down\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#foot-down\",\n    \"aria-label\": \"foot down permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Foot Down\"), mdx(\"p\", null, \"Trains a foot down network using sensor data from the legs.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Example\\n./b nbs footdown path/to/data-folder/\\n\")), mdx(\"h2\", {\n    \"id\": \"playing-back-data\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#playing-back-data\",\n    \"aria-label\": \"playing back data permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Playing Back Data\"), mdx(\"p\", null, \"Messages recorded to an NBS file can be played back to the system. When an NBS file is played back, the messages in it are emitted into the system at the same (relative) times they were recorded.\"), mdx(\"p\", null, \"Following on from the previous example, lets now run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"test/sensor.role\"), \" with recorded sensor data to analyse the results more closely without using the robot.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Remove any modules that emit the message/s you recorded. In this example, remove the SensorFilter module, since it emits \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Sensors\"), \". To do so, remove \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"input::SensorFilter\"), \" from the role.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"support::logging::DataPlayback\"), \" to the role.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add in the path to the NBS file in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"DataPlayback.yaml\"), \". If the file is in the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"recordings\"), \" directory, then just add in the file name.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add in the messages you are playing back in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"DataPlayback.yaml\"), \", in the same way it was done in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"DataLogging.yaml\"), \". In the example, change \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"message.input.Sensors\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"true\"), \".\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Recompile and run the role you are playing back the data with. In the example, rerun \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./b configure\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./b build\"), \", then run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./b run test/sensor\"), \" to locally run the role.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"For this example, it makes sense to view the robot in NUsight. Find out how to set up NUsight on the \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"/guides/main/getting-started#get-build-and-run-nusight\"\n  }, \"NUbook Getting Started page\"), \".\")), mdx(\"p\", null, \"Playing back recorded data is useful when:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Working remotely, where the data was previously recorded or recorded by another team member\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Testing a system with the same input data\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Not wanting to run the real robot continuously, where it is possible to use the same data\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Viewing the Visual Mesh in NUsight, due to bandwidth issues in sending it over the network\")));\n}\n;\nMDXContent.isMDXComponent = true;","tableOfContents":{"items":[{"url":"#recording-data","title":"Recording Data"},{"url":"#extracting-data","title":"Extracting Data","items":[{"url":"#stats","title":"Stats"},{"url":"#json","title":"JSON"},{"url":"#extract-images","title":"Extract Images"},{"url":"#video","title":"Video"},{"url":"#calibrate-cameras","title":"Calibrate Cameras"},{"url":"#foot-down","title":"Foot Down"}]},{"url":"#playing-back-data","title":"Playing Back Data"}]},"frontmatter":{"section":"Guides","chapter":"Main Codebase","title":"Data Recording and Playback","description":"How to record data from a running system and play it back.","keywords":null,"slug":"/guides/main/data-recording-playback","hidden":null},"childNUbookContributions":{"authors":[{"name":"Ysobel Sims","username":"ysims"}],"lastCommit":{"date":"2023-11-28T09:47:06.000Z","hash":"59078076843a88daa2a26c25bccfc46de77dfe7f"}}}},"pageContext":{"mdxPath":"src/book/03-guides/01-main-codebase/05-recording-playback.mdx","id":"00e1e61b-e337-5552-bee5-80a05c5555b3","next":{"chapter":"Main Codebase","title":"Camera Calibration","description":"Learn how to calibrate cameras.","slug":"/guides/main/camera-calibration","hidden":null},"previous":{"chapter":"Main Codebase","title":"Maintaining Subsystems","description":"How to maintain subsystems within the main codebase.","slug":"/guides/main/maintaining-subsystems","hidden":null},"menu":[{"title":"Team","slug":"/team/introduction","chapters":[{"title":"Introduction","slug":"/team/introduction","pages":[{"title":"Introduction to NUbots","slug":"/team/introduction","hidden":null},{"title":"Areas of Research","slug":"/team/areas-of-research","hidden":null},{"title":"Current Team","slug":"/team/current-members","hidden":null}],"hidden":false},{"title":"Joining the Team","slug":"/team/how-we-work","pages":[{"title":"How We Work","slug":"/team/how-we-work","hidden":null},{"title":"How to Join NUbots","slug":"/team/how-to-join","hidden":null},{"title":"Lab Induction","slug":"/team/induction","hidden":null}],"hidden":false},{"title":"Community","slug":"/team/sponsors","pages":[{"title":"Sponsors","slug":"/team/sponsors","hidden":null},{"title":"Connect","slug":"/team/connect","hidden":null}],"hidden":false},{"title":"History","slug":"/team/history","pages":[{"title":"Team History","slug":"/team/history","hidden":null},{"title":"Past Members","slug":"/team/past-members","hidden":null},{"title":"Publications","slug":"/team/publications","hidden":null}],"hidden":false},{"title":"RoboCup","slug":"/team/robocup","pages":[{"title":"RoboCup","slug":"/team/robocup","hidden":null},{"title":"Resources","slug":"/team/robocup/resources","hidden":null},{"title":"Debriefs","slug":"/team/robocup/debriefs","hidden":null}],"hidden":false}],"hidden":false},{"title":"System","slug":"/system/foundations/overview","chapters":[{"title":"Foundations","slug":"/system/foundations/overview","pages":[{"title":"Overview","slug":"/system/foundations/overview","hidden":null},{"title":"Build System","slug":"/system/foundations/build-system","hidden":null},{"title":"Continuous Integration","slug":"/system/foundations/ci-system","hidden":null},{"title":"NUClear","slug":"/system/foundations/nuclear","hidden":null},{"title":"Mathematics","slug":"/system/foundations/mathematics","hidden":null},{"title":"Configuration and Script System","slug":"/system/foundations/config-script","hidden":null},{"title":"Director","slug":"/system/foundations/director","hidden":null}],"hidden":false},{"title":"Subsystems","slug":"/system/subsystems/input","pages":[{"title":"Input","slug":"/system/subsystems/input","hidden":null},{"title":"Odometry","slug":"/system/subsystems/odometry","hidden":null},{"title":"Localisation","slug":"/system/subsystems/localisation","hidden":null},{"title":"Motion","slug":"/system/subsystems/motion","hidden":null},{"title":"Vision","slug":"/system/subsystems/vision","hidden":null},{"title":"Behaviour","slug":"/system/subsystems/behaviour","hidden":null},{"title":"Logging","slug":"/system/subsystems/logging","hidden":null}],"hidden":false},{"title":"Tools","slug":"/system/tools/nusight","pages":[{"title":"NUsight","slug":"/system/tools/nusight","hidden":null},{"title":"NUbook","slug":"/system/tools/nubook","hidden":null},{"title":"NUpbr","slug":"/system/tools/nupbr","hidden":null},{"title":"NUgan","slug":"/system/tools/nugan","hidden":null},{"title":"System Configuration","slug":"/system/tools/system_configuration","hidden":null},{"title":"NatNet SDK","slug":"/system/tools/natnet_sdk","hidden":null}],"hidden":false},{"title":"Hardware","slug":"/system/hardware/overview","pages":[{"title":"Overview and Specifications","slug":"/system/hardware/overview","hidden":null}],"hidden":false},{"title":"Modules","slug":"/system/modules/actuation/","pages":[{"title":"Actuation","slug":"/system/modules/actuation/","hidden":null},{"title":"Extension","slug":"/system/modules/extension/","hidden":null},{"title":"Input","slug":"/system/modules/input/","hidden":null},{"title":"Localisation","slug":"/system/modules/localisation/","hidden":null},{"title":"Nbs","slug":"/system/modules/nbs/","hidden":null},{"title":"Network","slug":"/system/modules/network/","hidden":null},{"title":"Output","slug":"/system/modules/output/","hidden":null},{"title":"Planning","slug":"/system/modules/planning/","hidden":null},{"title":"Platform","slug":"/system/modules/platform/","hidden":null},{"title":"Purpose","slug":"/system/modules/purpose/","hidden":null},{"title":"Skill","slug":"/system/modules/skill/","hidden":null},{"title":"Strategy","slug":"/system/modules/strategy/","hidden":null},{"title":"Support","slug":"/system/modules/support/","hidden":null},{"title":"Tools","slug":"/system/modules/tools/","hidden":null},{"title":"Vision","slug":"/system/modules/vision/","hidden":null}],"hidden":false}],"hidden":false},{"title":"Guides","slug":"/guides/main/getting-started","chapters":[{"title":"Main Codebase","slug":"/guides/main/getting-started","pages":[{"title":"Getting Started","slug":"/guides/main/getting-started","hidden":null},{"title":"NUClear","slug":"/guides/main/nuclear-tutorial","hidden":null},{"title":"Running and Tuning Scripts","slug":"/guides/main/tuning-and-running-scripts","hidden":null},{"title":"Maintaining Subsystems","slug":"/guides/main/maintaining-subsystems","hidden":null},{"title":"Data Recording and Playback","slug":"/guides/main/data-recording-playback","hidden":null},{"title":"Camera Calibration","slug":"/guides/main/camera-calibration","hidden":null},{"title":"Onboarding Workshop","slug":"/guides/main/onboarding","hidden":null}],"hidden":false},{"title":"Tools","slug":"/guides/tools/nusight-contribution","pages":[{"title":"Contributing to NUsight","slug":"/guides/tools/nusight-contribution","hidden":null},{"title":"Setting Up Webots","slug":"/guides/tools/webots-setup","hidden":null},{"title":"Visual Mesh Getting Started","slug":"/guides/tools/visualmesh","hidden":null},{"title":"Using NUpbr","slug":"/guides/tools/nupbr-guide","hidden":null},{"title":"GameController Setup","slug":"/guides/tools/gamecontroller","hidden":null}],"hidden":false},{"title":"Hardware","slug":"/guides/hardware/working-with-robots","pages":[{"title":"Working with Robots","slug":"/guides/hardware/working-with-robots","hidden":null},{"title":"Flashing a Robot","slug":"/guides/hardware/flashing","hidden":null},{"title":"Servo Setup and Calibration","slug":"/guides/hardware/servo-calibration","hidden":null},{"title":"Batteries","slug":"/guides/hardware/batteries","hidden":null},{"title":"DARwIn Op2 Robot Restoration and Calibration Guide","slug":"/guides/hardware/darwin-op2-guide","hidden":null}],"hidden":false},{"title":"General","slug":"/guides/general/learning-resources","pages":[{"title":"Learning Resources","slug":"/guides/general/learning-resources","hidden":null},{"title":"Contribution Workflow","slug":"/guides/general/contribute","hidden":null},{"title":"RoboCup Setup","slug":"/guides/general/robocup-setup","hidden":null},{"title":"Troubleshooting","slug":"/guides/general/troubleshooting","hidden":null},{"title":"Code Conventions","slug":"/guides/general/code-conventions","hidden":null},{"title":"Glossary","slug":"/guides/general/glossary","hidden":null}],"hidden":false}],"hidden":false},{"title":"Kitchen Sink","slug":"/kitchen-sink/headers","chapters":[{"title":"Markdown","slug":"/kitchen-sink/headers","pages":[{"title":"Headers","slug":"/kitchen-sink/headers","hidden":true},{"title":"Formatting and Paragraphs","slug":"/kitchen-sink/formatting-and-paragraphs","hidden":true},{"title":"Blockquotes","slug":"/kitchen-sink/blockquotes","hidden":true},{"title":"Alerts","slug":"/kitchen-sink/alerts","hidden":true},{"title":"Images","slug":"/kitchen-sink/images","hidden":true},{"title":"Lists","slug":"/kitchen-sink/lists","hidden":true},{"title":"Code","slug":"/kitchen-sink/code","hidden":true},{"title":"Math","slug":"/kitchen-sink/math","hidden":true},{"title":"Table of Contents","slug":"/kitchen-sink/table-of-contents","hidden":true},{"title":"Tables","slug":"/kitchen-sink/tables","hidden":true},{"title":"Collapsible Content","slug":"/kitchen-sink/collapsible","hidden":true},{"title":"GraphViz Diagrams","slug":"/kitchen-sink/graphviz-diagrams","hidden":true},{"title":"Referencing","slug":"/kitchen-sink/referencing","hidden":true}],"hidden":true}],"hidden":true}],"references":null,"hidden":null}},
    "staticQueryHashes": ["3715292327","445096115","466146812"]}