diff --git a/thesis/draft/Chapters/Chapter1-Introduction.tex b/thesis/draft/Chapters/Chapter1-Introduction.tex index 2dff587..5226bb4 100644 --- a/thesis/draft/Chapters/Chapter1-Introduction.tex +++ b/thesis/draft/Chapters/Chapter1-Introduction.tex @@ -9,35 +9,69 @@ % I am trying to persuade someone that a sound-based smart cube is viable on consumer grade hardware. \section{The Advent of Smart Cubes} -Speedsolving, the sport of solving twisty puzzles like the Rubik’s Cube as fast as possible, has seen a resurgence of popularity since the early 2000s. \cite{TODO} -Over the past two decades many advances in cube technology have produced ever higher performing puzzles. -Recently, the speedcubing community has seen the entrance of smart cubes, special versions of a Rubik’s Cube built around hardware that can connect to a mobile device over Bluetooth. -These smart cubes have sparked a wave of excitement with the vast opportunities they offer for automatic turn tracking, performance analysis, personalized improvement feedback, and networked competition. +Speedsolving, the sport of solving twisty puzzles like the Rubik’s Cube +as fast as possible, has seen a resurgence of popularity since the +early 2000s. \cite{TODO} Over the past two decades many advances in +cube technology have produced ever higher performing puzzles. + +Recently, the speedcubing community has seen the entrance of smart +cubes, special versions of a Rubik’s Cube built around hardware that +can connect to a mobile device over Bluetooth. These smart cubes have +sparked a wave of excitement with the vast opportunities they offer for +automatic turn tracking, performance analysis, personalized improvement +feedback, and networked competition. \section{Obstacles to Adoption} -While a revolutionary idea, smart cubes still face several obstacles to widespread adoption. + +While a revolutionary idea, smart cubes still face several obstacles to +widespread adoption. \begin{itemize} - \item \emph{Cost}: Smart cubes can cost up to eight times as much as a comparable non-smart speedcube. \footnote{For example, one popular budget speedcube, the Moyu Weilong, costs only \$5, while the cheapest smartcube, the Giiker Cube, starts at \$40. \cite{TODO}. On the higher end, a premium speedcube, like the Gans 356 XS, retails for just over \$60 while a premium smartcube, like the GoCube, retails for over \$100. \cite{TODO}} - \item \emph{Performance}: Existing smart cubes turn slower than comparable non-smart cubes. \cite{TODO} - \item \emph{Reliability}: Many smart cube owners report inability to connect the smart cube to a mobile device and missed/inaccurate turn tracking. \cite{TODO} - \item \emph{Regulation}: Current competition rules ban the use of electronics during timed solves, thus banning the use of smart cubes. There is no foreseeable change to this rule. \cite{TODO} % WCA Regulations + + \item \emph{Cost}: Smart cubes can cost up to eight times as much + as a comparable non-smart speedcube. \footnote{For example, one + popular budget speedcube, the Moyu Weilong, costs only \$5, while + the cheapest smartcube, the Giiker Cube, starts at \$40. + \cite{TODO}. On the higher end, a premium speedcube, like the Gans + 356 XS, retails for just over \$60 while a premium smartcube, like + the GoCube, retails for over \$100. \cite{TODO}} + + \item \emph{Performance}: Existing smart cubes turn slower than + comparable non-smart cubes. \cite{TODO} + + \item \emph{Reliability}: Many smart cube owners report inability + to connect the smart cube to a mobile device and missed/inaccurate + turn tracking. \cite{TODO} + + \item \emph{Regulation}: Current competition rules ban the use of + electronics during timed solves, thus banning the use of smart + cubes. There is no foreseeable change to this rule. \cite{TODO} % + WCA Regulations + \end{itemize} -As a result of these obstacles, many speedcubers refrain from purchasing a smart cube, despite expressing significant interest in the opportunities smart cubes offer. +As a result of these obstacles, many speedcubers refrain from +purchasing a smart cube, despite expressing significant interest in the +opportunities smart cubes offer. -Furthermore, all current smartcubes have been specifically built for the primary purpose of providing move-tracking functionality. -There is no existing way to automatically track the moves of a standard, "non-smart" speedcube. +Furthermore, all current smartcubes have been specifically built for +the primary purpose of providing move-tracking functionality. There is +no existing way to automatically track the moves of a standard, +"non-smart" speedcube. \section{Purpose of this Thesis} -The primary goal of this thesis is to create a proof-of-concept for a smart cube design that can enable a speedcuber to use his/her personal favorite cube, while still having all the benefits of a smart cube. +The primary goal of this thesis is to create a proof-of-concept for a +smart cube design that can enable a speedcuber to use his/her personal +favorite cube, while still having all the benefits of a smart cube. In other words, this thesis seeks to answer the following question: -\emph{Is it possible to track the face turns of a standard, "non-smart" speedcube in a non-destructive, competition-legal way?} +\emph{Is it possible to track the face turns of a standard, "non-smart" +speedcube in a non-destructive, competition-legal way?} \section{Thesis Overview} + TODO give an overview of the rest of the Thesis document. \ No newline at end of file diff --git a/thesis/draft/Chapters/Chapter2-Background.tex b/thesis/draft/Chapters/Chapter2-Background.tex index 90c6773..f584396 100644 --- a/thesis/draft/Chapters/Chapter2-Background.tex +++ b/thesis/draft/Chapters/Chapter2-Background.tex @@ -4,27 +4,39 @@ \label{Chapter2} % Change X to a consecutive number; for referencing this chapter elsewhere, use \ref{ChapterX} -TODO Each chapter starts with a paragraph that briefly outlines the purpose of each of the sections. +TODO Each chapter starts with a paragraph that briefly outlines the +purpose of each of the sections. \section{A Brief History of the Rubik's Cube} \label{sec:rubiks-history} -In 1974, Erno Rubik, a Hungarian professor of architecture, sought to help his students visualize space in three dimensions. -To that end, he created a special cube whose faces could independently rotate around all three physical axes \cite{rubik-motivation}. -When he added colored stickers to further aid in visualizing the movements, Mr. Rubik realized he had created a new puzzle. -He patented his cube in 1975, \cite{rubik-patent} and since then over 450 million units have been sold \cite{forbes-rubik-merger}, allowing an estimated 1 in 7 humans on earth to try their hand at solving it \cite{rubik-population-reached}. -Since then, the cube has been the subject of academic research, competition, leisure, and cultural iconography. +In 1974, Erno Rubik, a Hungarian professor of architecture, sought to +help his students visualize space in three dimensions. To that end, he +created a special cube whose faces could independently rotate around +all three physical axes \cite{rubik-motivation}. When he added colored +stickers to further aid in visualizing the movements, Mr. Rubik +realized he had created a new puzzle. He patented his cube in 1975, +\cite{rubik-patent} and since then over 450 million units have been +sold \cite{forbes-rubik-merger}, allowing an estimated 1 in 7 humans on +earth to try their hand at solving it \cite{rubik-population-reached}. + +Since then, the cube has been the subject of academic research, +competition, leisure, and cultural iconography. \section{The Anatomy of a Rubik's Cube} \label{sec:rubiks-anatomy} -The Rubik's Cube, like a standard geometric cube, has six faces, all of which are squares and positioned at right angles to each other. -When solved, each of these faces has a single, unique color. -The Rubik's Cube is further subdivided into a 3x3x3 arrangement of smaller "cubies" such that each face consists of nine individual colored stickers/tiles. -There are three different types of cubies: centers, edges, and corners. -Each type of cubie is distinguished by the number of unique colors it binds together into a single physical unit. +The Rubik's Cube, like a standard geometric cube, has six faces, all of +which are squares and positioned at right angles to each other. When +solved, each of these faces has a single, unique color. + +The Rubik's Cube is further subdivided into a 3x3x3 arrangement of +smaller "cubies" such that each face consists of nine individual +colored stickers/tiles. There are three different types of cubies: +centers, edges, and corners. Each type of cubie is distinguished by the +number of unique colors it binds together into a single physical unit. \begin{table}[h] \centering @@ -45,14 +57,19 @@ Each type of cubie is distinguished by the number of unique colors it binds toge \end{minipage} \end{table} -Each of the six center cubies are also attached to a common core which allows them to rotate freely, but fixes their position relative to each other. -As such, the single color of each center cubie is also the color shared by the corresponding face when the entire cube is solved. +Each of the six center cubies are also attached to a common core which +allows them to rotate freely, but fixes their position relative to each +other. As such, the single color of each center cubie is also the color +shared by the corresponding face when the entire cube is solved. \subsection{Algorithm Notation} TODO \subsection{The Laws of the Cube} -TODO Describe the basic concepts of group theory that stipulate what positions are and aren't legal. It might also be fun to discuss the derivation of the 43 quintillion possible positions on the cube. + +TODO Describe the basic concepts of group theory that stipulate what +positions are and aren't legal. It might also be fun to discuss the +derivation of the 43 quintillion possible positions on the cube. \section{Speedsolving} @@ -72,4 +89,8 @@ TODO \subsubsection{Competition Regulations} \label{subsec:competition-regulations} -According to WCA regulation 2i, "While competing, competitors must not use electronics or audio equipment (e.g. cell phones, MP3 players, dictaphones, additional lighting) apart from the Stackmat timer or stopwatch." \cite{wca-regulations} + +According to WCA regulation 2i, "While competing, competitors must not +use electronics or audio equipment (e.g. cell phones, MP3 players, +dictaphones, additional lighting) apart from the Stackmat timer or +stopwatch." \cite{wca-regulations} diff --git a/thesis/draft/Chapters/Chapter3-State-of-the-Art.tex b/thesis/draft/Chapters/Chapter3-State-of-the-Art.tex index cfc19c4..e2942cd 100644 --- a/thesis/draft/Chapters/Chapter3-State-of-the-Art.tex +++ b/thesis/draft/Chapters/Chapter3-State-of-the-Art.tex @@ -4,30 +4,58 @@ \section{Introduction} -This chapter seeks to provide a comprehensive summary of the existing approaches to tracking the face turns of a Rubik's Cube. -The most widely used solutions to date are found in Commercial Smartcubes (\ref{sec:commercial-smartcubes}), but significant research has also been carried out into Computer Vision based solutions (\ref{subsec:computer-vision}). -Other researchers have also explored the use of magnetic resonance and a muscle-tracking armband (\ref{sec:other-research}) -This chapter will also explore a selection of wireless communication techniques that at the time of writing have not been applied to the challenge of tracking the moves of a Rubik's Cube. -Specifically, this chapter will review the potential usage of sound (\ref{subsec:sound}), Radio Frequency Identification (RFID) (\ref{subsec:rfid}), and Off-Axis Magnetic Angle Sensors (\ref{subsec:magnetic-angle-sensors}). +This chapter seeks to provide a comprehensive summary of the existing +approaches to tracking the face turns of a Rubik's Cube. The most +widely used solutions to date are found in Commercial Smartcubes +(\ref{sec:commercial-smartcubes}), but significant research has also +been carried out into Computer Vision based solutions +(\ref{subsec:computer-vision}). Other researchers have also explored +the use of magnetic resonance and a muscle-tracking armband +(\ref{sec:other-research}) -Finally, this chapter will close by detailing the specific research questions this thesis will seek to answer (\ref{sec:research-questions}). +This chapter will also explore a selection of wireless communication +techniques that at the time of writing have not been applied to the +challenge of tracking the moves of a Rubik's Cube. Specifically, this +chapter will review the potential usage of sound (\ref{subsec:sound}), +Radio Frequency Identification (RFID) (\ref{subsec:rfid}), and Off-Axis +Magnetic Angle Sensors (\ref{subsec:magnetic-angle-sensors}). + +Finally, this chapter will close by detailing the specific research +questions this thesis will seek to answer +(\ref{sec:research-questions}). \section{Commercial Smartcubes} \label{sec:commercial-smartcubes} -Commercial Smartcubes are special Rubik's Cubes built around sensors that can detect face turns and transmit that information over Bluetooth. -Some models can also measure and transmit data about the cube's orientation. -At the time of writing, there are four major smartcubes on the market: the Giiker Cube, the Go Cube, the Rubik's Connected (which is powered by GoCube technology) and the Gans 356i. -This section will discuss the internal components of each of these cubes that provide this move-tracking functionality. +Commercial Smartcubes are special Rubik's Cubes built around sensors +that can detect face turns and transmit that information over +Bluetooth. Some models can also measure and transmit data about the +cube's orientation. + +At the time of writing, there are four major smartcubes on the market: +the Giiker Cube, the Go Cube, the Rubik's Connected (which is powered +by GoCube technology) and the Gans 356i. This section will discuss the +internal components of each of these cubes that provide this +move-tracking functionality. \subsection{Giiker Cube} -The Xiaomi Giiker Cube was released in September 2018 making it the first commercial smartcube on the market \cite{giiker-thecubicle}. -This "Supercube" as it was branded, used a relatively simple system for tracking the cube's movements. -The core of the cube is built around a small circuit board with a microcontroller that measures the cube's movements and a Bluetooth antenna that transmits those moves wirelessly (Figure ~\ref{fig:giiker-internal-board}). -The microcontroller detects each face turn by reading the voltage drop across a small circuit embedded within each center cap (Figure ~\ref{fig:giiker-center-front}). -The center cap circuit controls its output voltage by using a copper brush to change between four separate electric paths, three different resistors and ground, as each face rotates (Figure ~\ref{fig:giiker-center-brush}). \cite{giiker-internals} + +The Xiaomi Giiker Cube was released in September 2018 making it the +first commercial smartcube on the market \cite{giiker-thecubicle}. This +"Supercube" as it was branded, used a relatively simple system for +tracking the cube's movements. The core of the cube is built around a +small circuit board with a microcontroller that measures the cube's +movements and a Bluetooth antenna that transmits those moves wirelessly +(Figure ~\ref{fig:giiker-internal-board}). The microcontroller detects +each face turn by reading the voltage drop across a small circuit +embedded within each center cap (Figure +~\ref{fig:giiker-center-front}). The center cap circuit controls its +output voltage by using a copper brush to change between four separate +electric paths, three different resistors and ground, as each face +rotates (Figure ~\ref{fig:giiker-center-brush}). +\cite{giiker-internals} % How to do a sub-figure: https://tex.stackexchange.com/a/37597 \begin{figure}[h] @@ -61,6 +89,7 @@ The center cap circuit controls its output voltage by using a copper brush to ch \end{figure} \subsection{Go Cube} + Announced on Kickstarter in June 2018, Patricula's GoCube was the first smartcube to include a gyroscope that would track a Rubik's Cube's orientation in addition to the face turns applied to it. \cite{gocube-product-launch-video} Like the Giiker Cube before it, the GoCube's core contains a small circuit board with the main electronics including a microcontroller, Bluetooth antenna, and the added gyroscope (Figure ~\ref{fig:gocube-core}). Though the teardown pictures from the Go Cube's FCC filing aren't particularly clear, it appears that the cube registers face turns similarly to the Giiker Cube: by producing a voltage drop via changing which one of the four resistors shown across the bottom of the center cap board in Figure ~\ref{fig:gocube-cap-chip} is in series with the circuit. @@ -93,8 +122,14 @@ GoCube also serves as the underlying technology for the Rubik's Connected, the o \end{figure} \subsection{Gans 356i} -Released in July 2019, the Gans 356i was the first commercial smartcube produced by a traditional speedcube manufacturer. \cite{gans356i-thecubicle} -While the Gans 356i also uses a microcontroller to process the face turns and Bluetooth to transmit the move data, it tracks moves not through changing resistors in and out of a circuit, but via six plastic rods that connect the outer center caps to internal rotary encoders (Figure ~\ref{fig:gans356i-core}). + +Released in July 2019, the Gans 356i was the first commercial smartcube +produced by a traditional speedcube manufacturer. +\cite{gans356i-thecubicle} While the Gans 356i also uses a +microcontroller to process the face turns and Bluetooth to transmit the +move data, it tracks moves not through changing resistors in and out of +a circuit, but via six plastic rods that connect the outer center caps +to internal rotary encoders (Figure ~\ref{fig:gans356i-core}). \begin{figure}[h] \centering @@ -106,37 +141,83 @@ While the Gans 356i also uses a microcontroller to process the face turns and Bl \section{Academia} \label{sec:academia} -In addition to the various commercial smartcubes, many academic research projects have involved some element of tracking the state/face turns of a Rubik's Cube. -This section summarizes the current state of academic research into using computer vision, magnetic resonance, and a muscle-tracking armband to track the state of a Rubik's Cube. +In addition to the various commercial smartcubes, many academic +research projects have involved some element of tracking the state/face +turns of a Rubik's Cube. + +This section summarizes the current state of academic research into +using computer vision, magnetic resonance, and a muscle-tracking +armband to track the state of a Rubik's Cube. \subsection{Computer Vision} \label{subsec:computer-vision} -Computer Vision refers to the "field of Artificial Intelligence (AI) that enables computers and systems to derive meaningful information from digital images, videos, and other visual inputs." \cite{ibm-cv-definition} -Since human manipulation of a Rubik's Cube is a physical, observable process, Computer Vision algorithms could be developed to extract face turn information from videos of Rubik's Cube solutions. -This section summarizes some of the relevant research in this area, including computer vision algorithms capable of extracting individual sticker colors from video, measuring the angle of rotation of a specific face, and detection of entire face turns and face turn sequences. +Computer Vision refers to the "field of Artificial Intelligence (AI) +that enables computers and systems to derive meaningful information +from digital images, videos, and other visual inputs." +\cite{ibm-cv-definition} Since human manipulation of a Rubik's Cube is +a physical, observable process, Computer Vision algorithms could be +developed to extract face turn information from videos of Rubik's Cube +solutions. + +This section summarizes some of the relevant research in this area, +including computer vision algorithms capable of extracting individual +sticker colors from video, measuring the angle of rotation of a +specific face, and detection of entire face turns and face turn +sequences. \subsubsection{Sticker Color Classification} -In 2015, Jay Hack, a graduate student studying Computer Science at Stanford developed a neural network capable of recognizing the colors of a Rubik's Cube face from video in various lighting conditions. -His algorithm could classify frames within 7 milliseconds with 92\% accuracy. \cite{hackrubik} + +In 2015, Jay Hack, a graduate student studying Computer Science at +Stanford developed a neural network capable of recognizing the colors +of a Rubik's Cube face from video in various lighting conditions. His +algorithm could classify frames within 7 milliseconds with 92\% +accuracy. \cite{hackrubik} \subsubsection{Measuring a Face's Angle of Rotation} -In 2019, OpenAI et al. published a viral video of a robot hand that had taught itself to solve a Rubik's Cube. -While the final, most successful version of the robot hand's software used a Giiker Cube to obtain the current rotational state of the cube, OpenAI et al. also researched the viability of tracking a Rubik's Cube's position using only computer vision. -Their most successful vision-only algorithm measured only the rotation angle of the top-most face on the Rubik's Cube and assumed significant hardware requirements: a modified sticker set for the Rubik's Cube, a well-lit environment, three strategically positioned RBG Basler cameras, and a neural network trained on "a pool of optimizer nodes, each of which uses 8 NVIDIA V100 GPUs and 64 CPU cores". -At peak performance, their vision-only algorithm's average error (the difference between the predicted face angle and the actual face angle) was 15.92$^\circ$, nearly three times the 5.90$^\circ$ average error of the hardware-based face angle measurement. \cite{openai2019rubiks} + +In 2019, OpenAI et al. published a viral video of a robot hand that had +taught itself to solve a Rubik's Cube. While the final, most successful +version of the robot hand's software used a Giiker Cube to obtain the +current rotational state of the cube, OpenAI et al. also researched the +viability of tracking a Rubik's Cube's position using only computer +vision. Their most successful vision-only algorithm measured only the +rotation angle of the top-most face on the Rubik's Cube and assumed +significant hardware requirements: a modified sticker set for the +Rubik's Cube, a well-lit environment, three strategically positioned +RBG Basler cameras, and a neural network trained on "a pool of +optimizer nodes, each of which uses 8 NVIDIA V100 GPUs and 64 CPU +cores". At peak performance, their vision-only algorithm's average +error (the difference between the predicted face angle and the actual +face angle) was 15.92$^\circ$, nearly three times the 5.90$^\circ$ +average error of the hardware-based face angle measurement. +\cite{openai2019rubiks} \subsubsection{Classification of Single Moves and Entire Move Sequences} -In 2020, Junshen Kevin Chen, Wanze Xie, and Zhouheng Sun, graduate Computer Science students at Stanford created the DeepCube dataset consisting of over 20,000 videos of Rubik's Cube face turns with consistent lighting and backgrounds. -They also built a neural network to classify the videos with the face turn they contained. -Their best performing model only made "one mistake every 15 moves" which corresponds to a 93.3\% accuracy. \cite{chendeepcube} + +In 2020, Junshen Kevin Chen, Wanze Xie, and Zhouheng Sun, graduate +Computer Science students at Stanford created the DeepCube dataset +consisting of over 20,000 videos of Rubik's Cube face turns with +consistent lighting and backgrounds. They also built a neural network +to classify the videos with the face turn they contained. Their best +performing model only made "one mistake every 15 moves" which +corresponds to a 93.3\% accuracy. \cite{chendeepcube} \subsection{Magnetic Resonance} -In 2018, Maria Mannone et al. used the IM3D magnetic 3D motion tracking technology introduced by Huang et al. \cite{im3d} to track the state of a Rubik's Cube across various movements for the purpose of generating a sequence of musical chords. -This approach to turn tracking requires a special array of magnetic coils as shown in Figure ~\ref{fig:im3d-architecture} and the installation of "multiple small, light-weight, wireless markers (LC coils) with unique IDs" (a process that requires permanent modifications to the cube as evidenced by the damaged plastic in Figure ~\ref{fig:cubeharmonic-trackers}). -Mannone et al. reported no issues with mistakes in this move tracking technology. \cite{mannone-cubeharmonic-2018} + +In 2018, Maria Mannone et al. used the IM3D magnetic 3D motion tracking +technology introduced by Huang et al. \cite{im3d} to track the state of +a Rubik's Cube across various movements for the purpose of generating a +sequence of musical chords. This approach to turn tracking requires a +special array of magnetic coils as shown in Figure +~\ref{fig:im3d-architecture} and the installation of "multiple small, +light-weight, wireless markers (LC coils) with unique IDs" (a process +that requires permanent modifications to the cube as evidenced by the +damaged plastic in Figure ~\ref{fig:cubeharmonic-trackers}). Mannone et +al. reported no issues with mistakes in this move tracking technology. +\cite{mannone-cubeharmonic-2018} % How to do a sub-figure: https://tex.stackexchange.com/a/37597 \begin{figure}[h] @@ -158,32 +239,80 @@ Mannone et al. reported no issues with mistakes in this move tracking technology \end{figure} \subsection{Muscle-Tracking Armband} -In 2017, Richard Polfreman and Benjamin Oliver researched ways to use the face turns of a Rubik's Cube as controls for a music synthesizer. They explored the use of a muscle-tracking armband (specifically the Myo Armband) to track the human solver's finger movements while manipulating the cube. However, since "the Myo moved a little when 'cubing'", they ultimately found greater success with a computer vision based tracking solution similar to those discussed in \ref{subsec:computer-vision}. + +In 2017, Richard Polfreman and Benjamin Oliver researched ways to use +the face turns of a Rubik's Cube as controls for a music synthesizer. +They explored the use of a muscle-tracking armband (specifically the +Myo Armband) to track the human solver's finger movements while +manipulating the cube. However, since "the Myo moved a little when +'cubing'", they ultimately found greater success with a computer vision +based tracking solution similar to those discussed in +\ref{subsec:computer-vision}. \section{Other Relevant Research} \label{sec:other-research} -Finally, there are a number of research papers/commercial products that seek to transmit data in highly-constrained environments that are potentially relevant to the challenge of tracking the turns of a Rubik's Cube. -This section discusses some of these potential alternate move tracking mediums, specifically sound, RFID, and off-angle magnetic rotation sensors. +Finally, there are a number of research papers/commercial products that +seek to transmit data in highly-constrained environments that are +potentially relevant to the challenge of tracking the turns of a +Rubik's Cube. + +This section discusses some of these potential alternate move tracking +mediums, specifically sound, RFID, and off-angle magnetic rotation +sensors. \subsection{Sound} \label{subsec:sound} -Sound is another communication medium that could be leveraged to track the moves of a Rubik's Cube. In 2015, Jonas Michel, a researcher at The University of Texas at Austin documented his exploration of the viability of creating an "acoustic modem" to transmit an arbitrary sequence of bits using sound. -He observed that "as commercial off-the-shelf (COTS) smartphones become more powerful, it is worthwhile to revisit the use of sound as a medium for aerial digital device-to-device communications." \cite{michel-sound} -Indeed, since many speedcubers practice by timing solves on a microphone-equipped smartphone or laptop\footnote{One of the highest rated cubing timers on Android, Twisty Timer, has over 100,000 downloads on the Google Play Store \cite{googleplay-twistytimer}. By comparison, SpeedSolving.com, the central forum for speedcubing related discussion, has about 43,000 members \cite{speedsolving-com}}, sound is a promising alternative communication medium to existing Bluetooth-based smartcubes. +Sound is another communication medium that could be leveraged to track +the moves of a Rubik's Cube. In 2015, Jonas Michel, a researcher at The +University of Texas at Austin documented his exploration of the +viability of creating an "acoustic modem" to transmit an arbitrary +sequence of bits using sound. He observed that "as commercial +off-the-shelf (COTS) smartphones become more powerful, it is worthwhile +to revisit the use of sound as a medium for aerial digital +device-to-device communications." \cite{michel-sound} + +Indeed, since many speedcubers practice by timing solves on a +microphone-equipped smartphone or laptop\footnote{One of the highest +rated cubing timers on Android, Twisty Timer, has over 100,000 +downloads on the Google Play Store \cite{googleplay-twistytimer}. By +comparison, SpeedSolving.com, the central forum for speedcubing related +discussion, has about 43,000 members \cite{speedsolving-com}}, sound is +a promising alternative communication medium to existing +Bluetooth-based smartcubes. \subsection{Radio Frequency Identification (RFID)} \label{subsec:rfid} -Radio Frequency Identification (RFID) is a wireless technology often used in supply-chain systems \cite{rfid-rotary-encoder} based on individual tags that can transmit a fixed set of information to a nearby reader. \cite{fda-rfid}. -In 2018, Genovesi et al. proposed a rotary encoder based on RFID that produced angle measurements accurate to within 3$^\circ$ after a calibration that "must only be done once (when the sensor is put in place) if the distance between the reader and the tag does not change." \cite{rfid-rotary-encoder} -While this approach may not be particularly useful for tracking the moves of human speedcubers since their movement of the cube would require constant re-calibration, it could be used in robotics based applications desiring to track the ongoing state of the cube. +Radio Frequency Identification (RFID) is a wireless technology often +used in supply-chain systems \cite{rfid-rotary-encoder} based on +individual tags that can transmit a fixed set of information to a +nearby reader. \cite{fda-rfid}. In 2018, Genovesi et al. proposed a +rotary encoder based on RFID that produced angle measurements accurate +to within 3$^\circ$ after a calibration that "must only be done once +(when the sensor is put in place) if the distance between the reader +and the tag does not change." \cite{rfid-rotary-encoder} + +While this approach may not be particularly useful for tracking the +moves of human speedcubers since their movement of the cube would +require constant re-calibration, it could be used in robotics based +applications desiring to track the ongoing state of the cube. \subsection{Off-Axis Magnetic Angle Sensors} \label{subsec:magnetic-angle-sensors} -Another unique type of rotary encoder is an off-angle magnetic rotational sensor like the ones produced by NVE Corporation. \cite{nve-mag-sensor} In the context of a Rubik's Cube, a properly sized diametric ring magnet could be fastened to the inner screw below the center cap of each face of the cube and the resulting optimal position for the magnetic sensor would stay within the walls of the center cap as shown by the the $z_0$ and $R_0$ values in Figure ~\ref{fig:nve-mag-calculations} which are smaller than the 8 mm distance between the center screw and the inner wall of the center cap in a standard-sized cube like the Gans 356. + +Another unique type of rotary encoder is an off-angle magnetic +rotational sensor like the ones produced by NVE Corporation. +\cite{nve-mag-sensor} In the context of a Rubik's Cube, a properly +sized diametric ring magnet could be fastened to the inner screw below +the center cap of each face of the cube and the resulting optimal +position for the magnetic sensor would stay within the walls of the +center cap as shown by the the $z_0$ and $R_0$ values in Figure +~\ref{fig:nve-mag-calculations} which are smaller than the 8 mm +distance between the center screw and the inner wall of the center cap +in a standard-sized cube like the Gans 356. \begin{figure}[h] \centering @@ -195,16 +324,52 @@ Another unique type of rotary encoder is an off-angle magnetic rotational sensor \section{Research Questions} \label{sec:research-questions} -While there are many effective solutions for tracking the moves of a Rubik's Cube if the cube is built for that purpose, there does not yet exist a comparably effective technique for tracking the moves of a standard, "non-smart" Rubik's Cube. -As shown above, significant effort has been spent researching and developing solutions that can leverage the Bluetooth transmitters and camera sensors of consumer grade smartphones and laptops with varying degrees of success. -However, little to no research has been carried out exploring the viability of using the microphones readily available on the same devices for this purpose. -Thus, this thesis will seek to answer the overarching question from Chapter \ref{Chapter1} \emph{"Is it possible to track the face turns of a standard, "non-smart" speedcube in a non-destructive, competition-legal way?"} by detailing a proof-of-concept for a sound-based smartcube design created in response to the following questions: +While there are many effective solutions for tracking the moves of a +Rubik's Cube if the cube is built for that purpose, there does not yet +exist a comparably effective technique for tracking the moves of a +standard, "non-smart" Rubik's Cube. As shown above, significant effort +has been spent researching and developing solutions that can leverage +the Bluetooth transmitters and camera sensors of consumer grade +smartphones and laptops with varying degrees of success. However, +little to no research has been carried out exploring the viability of +using the microphones readily available on the same devices for this +purpose. + +Thus, this thesis will seek to answer the overarching question from +Chapter \ref{Chapter1} \emph{"Is it possible to track the face turns of +a standard, "non-smart" speedcube in a non-destructive, +competition-legal way?"} by detailing a proof-of-concept for a +sound-based smartcube design created in response to the following +questions: \begin{enumerate} - \item \textbf{Feasibility on Consumer Hardware}: \emph{What are the constraints for a sound-based smart cube design compatible with consumer-grade microphones like those found in common smartphones and laptops?} - \item \textbf{Compatibility with Standard Speedcubes}: \emph{How could such a sound-based smart cube design be deployed within a standard, "non-smart" speedcube without requiring permanent modifications to the original cube?} % TODO Make sure to not answer this with 100% certainty since we didn't build a fully functioning prototype. Also include in *outlook*. - \item \textbf{Move Tracking Accuracy}: \emph{How could such a sound-based smart cube design track the face turns of a Rubik's Cube with high accuracy?} % TODO In the evaluation section, use accuracy as a metric to compare options to each other. Then in the conclusion's answer to this question, state the final, best accuracy that we could get. Be mindful then of the conditions in which this accuracy was attained, using a specific speaker, microphone, and background environment. - \item \textbf{Move Tracking Granularity}: \emph{How could such a sound-based smart cube design record the time spent executing each individual face turn of a Rubik's Cube?} - \item \textbf{Competition Legality}: \emph{How could such a sound-based smartcube design comply with competition regulations prohibiting the use of electronics while performing a competitive solve?} + + \item \textbf{Feasibility on Consumer Hardware}: \emph{What are the + constraints for a sound-based smart cube design compatible with + consumer-grade microphones like those found in common smartphones + and laptops?} + + \item \textbf{Compatibility with Standard Speedcubes}: \emph{How + could such a sound-based smart cube design be deployed within a + standard, "non-smart" speedcube without requiring permanent + modifications to the original cube?} + + % TODO Make sure to not answer this with 100% certainty since we didn't build a fully functioning prototype. Also include in *outlook*. + + \item \textbf{Move Tracking Accuracy}: \emph{How could such a + sound-based smart cube design track the face turns of a Rubik's + Cube with high accuracy?} + + % TODO In the evaluation section, use accuracy as a metric to compare options to each other. Then in the conclusion's answer to this question, state the final, best accuracy that we could get. Be mindful then of the conditions in which this accuracy was attained, using a specific speaker, microphone, and background environment. + + \item \textbf{Move Tracking Granularity}: \emph{How could such a + sound-based smart cube design record the time spent executing each + individual face turn of a Rubik's Cube?} + + \item \textbf{Competition Legality}: \emph{How could such a + sound-based smartcube design comply with competition regulations + prohibiting the use of electronics while performing a competitive + solve?} + \end{enumerate} diff --git a/thesis/draft/Chapters/Chapter4-Protocol-Design.tex b/thesis/draft/Chapters/Chapter4-Protocol-Design.tex index 112779a..1b2be64 100644 --- a/thesis/draft/Chapters/Chapter4-Protocol-Design.tex +++ b/thesis/draft/Chapters/Chapter4-Protocol-Design.tex @@ -4,32 +4,59 @@ \section{Introduction} -Designing a sound-based protocol for tracking the moves of a Rubik's Cube requires great care. -Lots of things produce sound that could interfere with the protocol: people talking, machines operating, nature stirring, and so forth. -Furthermore, the structure of the Rubik's Cube itself imposes stringent physical constraints on the size of any components used to produce the sounds used in the protocol. -This chapter first seeks to clearly detail the specific constraints that must be considered when designing a sound-based protocol for tracking the moves of a Rubik's Cube (\ref{sec:protocol-requirements}). -From there, a specific protocol that meets these constraints will be proposed in Section \ref{sec:specification}. -This proposed protocol will then be contrasted with an alternative option in Section \ref{sec:alternatives}. -Finally, an overview of the plan to implement the proposed protocol in a proof-of-concept will be given in Section \ref{sec:protocol-summary} +Designing a sound-based protocol for tracking the moves of a Rubik's +Cube requires great care. Lots of things produce sound that could +interfere with the protocol: people talking, machines operating, nature +stirring, and so forth. Furthermore, the structure of the Rubik's Cube +itself imposes stringent physical constraints on the size of any +components used to produce the sounds used in the protocol. + +This chapter first seeks to clearly detail the specific constraints +that must be considered when designing a sound-based protocol for +tracking the moves of a Rubik's Cube (\ref{sec:protocol-requirements}). +From there, a specific protocol that meets these constraints will be +proposed in Section \ref{sec:specification}. This proposed protocol +will then be contrasted with an alternative option in Section +\ref{sec:alternatives}. Finally, an overview of the plan to implement +the proposed protocol in a proof-of-concept will be given in Section +\ref{sec:protocol-summary} \section{Requirements} \label{sec:protocol-requirements} -This section will detail the constraints required for designing a protocol for tracking the moves of a Rubik's Cube. -These constraints include a sufficiently strong signal-to-noise ratio (\ref{subsec:signal-to-noise-ratio}), sufficient distinctiveness between tones (\ref{subsec:tone-distinctiveness}), the frequency response range of consumer hardware (\ref{subsec:frequency-response-range}), and the human auditory range (\ref{subsec:human-auditory-range}). +This section will detail the constraints required for designing a +protocol for tracking the moves of a Rubik's Cube. + +These constraints include a sufficiently strong signal-to-noise ratio +(\ref{subsec:signal-to-noise-ratio}), sufficient distinctiveness +between tones (\ref{subsec:tone-distinctiveness}), the frequency +response range of consumer hardware +(\ref{subsec:frequency-response-range}), and the human auditory range +(\ref{subsec:human-auditory-range}). \subsection{Signal-to-Noise Ratio} \label{subsec:signal-to-noise-ratio} -Sound is an easily accessible, and therefore noisy, medium of communication. -As a result, any data transmission protocol based on sound must be resilient to the presence of additional noise unrelated to the signal being transmitted. -For a sound-based protocol to be effective, its tones must be easily distinguishable from background noise. -For the purpose of this thesis, "background noise" will be considered the ambient noise present in a quiet room when a speedcuber is actively solving a Rubik's Cube. -Measurement of the background noise was carried out by using the Android app Spectroid \cite{googleplay-spectroid} to create live visualizations of the background noise in a household bedroom (a typical place for a speedcuber to practice solving the cube) while solving three types of speedcubes selected based on their "noisiness". -A fourth visualization of just the ambient noise in the bedroom (i.e. while no cubes were being solved) was also recorded as a control. -These visualizations are shown in Figure \ref{fig:signal-to-noise-ratio}. +Sound is an easily accessible, and therefore noisy, medium of +communication. As a result, any data transmission protocol based on +sound must be resilient to the presence of additional noise unrelated +to the signal being transmitted. For a sound-based protocol to be +effective, its tones must be easily distinguishable from background +noise. + +For the purpose of this thesis, "background noise" will be considered +the ambient noise present in a quiet room when a speedcuber is actively +solving a Rubik's Cube. Measurement of the background noise was carried +out by using the Android app Spectroid \cite{googleplay-spectroid} to +create live visualizations of the background noise in a household +bedroom (a typical place for a speedcuber to practice solving the cube) +while solving three types of speedcubes selected based on their +"noisiness". A fourth visualization of just the ambient noise in the +bedroom (i.e. while no cubes were being solved) was also recorded as a +control. These visualizations are shown in Figure +\ref{fig:signal-to-noise-ratio}. % How to do a sub-figure: https://tex.stackexchange.com/a/37597 \begin{figure} @@ -71,22 +98,42 @@ These visualizations are shown in Figure \ref{fig:signal-to-noise-ratio}. \subsubsection{How to Read a Spectrogram} \label{subsubsec:how-to-read-a-spectrogram} -These visualizations are formally known as an audio "spectrogram", and show both the specific frequencies present in each audio sample and their intensity. -The graph on top shows the current and maximum strengths of each frequency present in the background audio with the yellow and red lines respectively. -The x-axis measures the specific frequencies in Hz, while the y-axis shows the intensity (aka loudness) of those frequencies in dB. +These visualizations are formally known as an audio "spectrogram", and +show both the specific frequencies present in each audio sample and +their intensity. -The lower graph is the actual spectrogram of the background audio. -It shares the same x-axis as the top graph, but its y-axis instead measures time with time = 0 at the bottom of the graph and time = now at the top. -Here, the brighter colors represent a more intense (aka louder) frequency with the exact key shown on the left side of the graph. As such, this lower graph can be considered a "bird's eye view" of the top graph over time. +The graph on top shows the current and maximum strengths of each +frequency present in the background audio with the yellow and red lines +respectively. The x-axis measures the specific frequencies in Hz, while +the y-axis shows the intensity (aka loudness) of those frequencies in +dB. + +The lower graph is the actual spectrogram of the background audio. It +shares the same x-axis as the top graph, but its y-axis instead +measures time with time = 0 at the bottom of the graph and time = now +at the top. Here, the brighter colors represent a more intense (aka +louder) frequency with the exact key shown on the left side of the +graph. As such, this lower graph can be considered a "bird's eye view" +of the top graph over time. \subsubsection{Key Observations from the Spectrogram} \label{subsubsec:key-observations-from-the-spectrogram} -As shown in Figure \ref{fig:signal-to-noise-ratio-silent}, the predominant background noises in a quiet bedroom are the tones between 80Hz - 500Hz which reach a max strength of -54dB. -In contrast, Figures \ref{fig:signal-to-noise-ratio-356} and \ref{fig:signal-to-noise-ratio-xs} show that solving a speedcube creates additional noise in the frequency ranges from 1000Hz - 20000Hz, with Figure \ref{fig:signal-to-noise-ratio-qiyi} showing particular strength from the noisy QiYi cube with the frequencies in the 1000Hz - 10000Hz range reaching up to -30dB. +As shown in Figure \ref{fig:signal-to-noise-ratio-silent}, the +predominant background noises in a quiet bedroom are the tones between +80Hz - 500Hz which reach a max strength of -54dB. + +In contrast, Figures \ref{fig:signal-to-noise-ratio-356} and +\ref{fig:signal-to-noise-ratio-xs} show that solving a speedcube +creates additional noise in the frequency ranges from 1000Hz - 20000Hz, +with Figure \ref{fig:signal-to-noise-ratio-qiyi} showing particular +strength from the noisy QiYi cube with the frequencies in the 1000Hz - +10000Hz range reaching up to -30dB. + +Thus, a sound-based move tracking protocol must account for the +following items in order to achieve an adequate signal-to-noise ratio: -Thus, a sound-based move tracking protocol must account for the following items in order to achieve an adequate signal-to-noise ratio: \begin{itemize} \item Tones between 500Hz and 1000Hz are the easiest to detect while solving a speedcube since they have the least competition from other background sounds while solving a speedcube. \item Tones between 1000Hz and 20000Hz must be significantly louder than -30dB or they risk being indistinguishable from the background noise created by a noisy speedcube like the QiYi Qimeng. @@ -94,13 +141,23 @@ Thus, a sound-based move tracking protocol must account for the following items \subsection{Tone Distinctiveness} \label{subsec:tone-distinctiveness} -If more than one tone is required for the protocol, each tone must be unique enough to be easily distinguished from each other tone. -Since a standard smartphone or laptop microphone will be used on the listening end of this protocol, the definition of "easily distinguishable" must be based on an assessment of how clearly smartphone and laptop-grade microphones can distinguish similar frequencies. -To measure the sensitivity of a standard smartphone microphone (in this case a Google Pixel 1), a recording was taken of two distinct tones that started 500 Hz apart and stepped closer together in 10 Hz increments every 0.5 seconds until their frequencies were identical. +If more than one tone is required for the protocol, each tone must be +unique enough to be easily distinguished from each other tone. Since a +standard smartphone or laptop microphone will be used on the listening +end of this protocol, the definition of "easily distinguishable" must +be based on an assessment of how clearly smartphone and laptop-grade +microphones can distinguish similar frequencies. + +To measure the sensitivity of a standard smartphone microphone (in this +case a Google Pixel 1), a recording was taken of two distinct tones +that started 500 Hz apart and stepped closer together in 10 Hz +increments every 0.5 seconds until their frequencies were identical. -As shown in Figure \ref{fig:tone-sep}, the two tones are clearly distinguishable from 500Hz apart to 150Hz apart. -However, the distinction began waning once the tones came within 100Hz of each other, and by 80Hz they became entirely indistinguishable. +As shown in Figure \ref{fig:tone-sep}, the two tones are clearly +distinguishable from 500Hz apart to 150Hz apart. However, the +distinction began waning once the tones came within 100Hz of each +other, and by 80Hz they became entirely indistinguishable. % How to do a sub-figure: https://tex.stackexchange.com/a/37597 \begin{figure}[h] @@ -137,18 +194,31 @@ However, the distinction began waning once the tones came within 100Hz of each o \end{subfigure}% \end{figure} -Thus, a sound-based move tracking protocol must account for the following items in order to achieve adequate tone distinctiveness: +Thus, a sound-based move tracking protocol must account for the +following items in order to achieve adequate tone distinctiveness: + \begin{itemize} - \item Tones should be separated by at least 100Hz in order to be clearly distinguished from other tones in the protocol. + + \item Tones should be separated by at least 100Hz in order to be + clearly distinguished from other tones in the protocol. + \end{itemize} \subsection{Frequency Response Range of Consumer Hardware} \label{subsec:frequency-response-range} -Since a standard smartphone or laptop microphone will be used on the listening end of this protocol, any tones used in the protocol must be within the range of tones that a smartphone or laptop-grade microphones can pick up, otherwise a more sophisticated, external microphone will need to be attached to the device. -This range is formally known as the "frequency response range" of the microphone. -According to a Stanford research paper \cite{typical-mic-range} that analyzed over 10,000 mobile devices, the typical smartphone microphone has a frequency response range of 20Hz to 20kHz as shown in Figure ~\ref{fig:freq-res-range}. +Since a standard smartphone or laptop microphone will be used on the +listening end of this protocol, any tones used in the protocol must be +within the range of tones that a smartphone or laptop-grade microphones +can pick up, otherwise a more sophisticated, external microphone will +need to be attached to the device. This range is formally known as the +"frequency response range" of the microphone. + +According to a Stanford research paper \cite{typical-mic-range} that +analyzed over 10,000 mobile devices, the typical smartphone microphone +has a frequency response range of 20Hz to 20kHz as shown in Figure +~\ref{fig:freq-res-range}. \begin{figure}[h] \centering @@ -157,31 +227,57 @@ According to a Stanford research paper \cite{typical-mic-range} that analyzed ov \includegraphics[width=.50\linewidth]{Figures/4 Protocol Design/Frequency Response Range/typical-smartphone-response-range.png} \end{figure} -Thus, a sound-based move tracking protocol must account for the following restrictions on which tones can be used for the protocol: +Thus, a sound-based move tracking protocol must account for the +following restrictions on which tones can be used for the protocol: + \begin{itemize} - \item Tones used in the protocol must fall on the range of 20Hz-20kHz in order to be detectable by a typical smartphone or laptop microphone. + + \item Tones used in the protocol must fall on the range of + 20Hz-20kHz in order to be detectable by a typical smartphone or + laptop microphone. + \end{itemize} \subsection{Human Auditory Range} \label{subsec:human-auditory-range} -An audible protocol could be distracting to a speedcuber. -The human ear can detect audible frequencies from 20Hz to 20kHz, though this usually degrades with age with many people unable to notice sounds above 16kHz. \cite{audible-range}. -However, not all tones are pleasant to listen to, particularly higher frequency tones. -Tones within the frequency range of a piano (27.5Hz - 4kHz) are generally considered acceptable, while tones above the piano's upper range are often irritating. \cite{piano-range} -Thus, a sound-based move tracking protocol should be considerate of the human ear's sensitivity to various frequencies: +An audible protocol could be distracting to a speedcuber. The human ear +can detect audible frequencies from 20Hz to 20kHz, though this usually +degrades with age with many people unable to notice sounds above 16kHz. +\cite{audible-range}. However, not all tones are pleasant to listen to, +particularly higher frequency tones. Tones within the frequency range +of a piano (27.5Hz - 4kHz) are generally considered acceptable, while +tones above the piano's upper range are often irritating. +\cite{piano-range} + +Thus, a sound-based move tracking protocol should be considerate of the +human ear's sensitivity to various frequencies: + \begin{itemize} - \item The most acceptable tones for human speedsolvers are in the musical range of up to 4kHz or above the standard audible range of 16kHz + + \item The most acceptable tones for human speedsolvers are in the + musical range of up to 4kHz or above the standard audible range of + 16kHz + \end{itemize} \section{Specification} \label{sec:specification} -Given the above constraints, this section will detail a sound-based protocol for tracking the moves of a Rubik's Cube by continuously transmitting the current state of the cube. In this protocol, changes to the cube's state cause a change in the transmitted tones which can be recorded and analyzed to determine the face turn applied. + +Given the above constraints, this section will detail a sound-based +protocol for tracking the moves of a Rubik's Cube by continuously +transmitting the current state of the cube. In this protocol, changes +to the cube's state cause a change in the transmitted tones which can +be recorded and analyzed to determine the face turn applied. \subsection{Representing the Cube's Current State} \label{subsec:representing-cube-state} -As mentioned in Section \ref{sec:rubiks-anatomy}, a Rubik's Cube has six centerpieces that are fixed relative to each other, but can each rotate freely through four possible rotational positions as shown in Figure \ref{fig:rotation-alignment}. + +As mentioned in Section \ref{sec:rubiks-anatomy}, a Rubik's Cube has +six centerpieces that are fixed relative to each other, but can each +rotate freely through four possible rotational positions as shown in +Figure \ref{fig:rotation-alignment}. \begin{figure}[h] \centering @@ -213,34 +309,66 @@ As mentioned in Section \ref{sec:rubiks-anatomy}, a Rubik's Cube has six centerp \end{subfigure}% \end{figure} -The state of a centerpiece is defined as its current rotational position. -Implicit in this definition is the fact that a centerpiece is guaranteed to occupy one and only one of its four possible states at any given time. - -Each of the six centerpieces has an independent set of four possible states, yielding a total of 24 different centerpiece states for the Rubik's Cube, of which there will always be exactly six active at any given time. - -It's important to note that a knowledge of the current state of each centerpiece does not imply knowledge of the exact state of each of edge and corner cubie. -For example, after applying the algorithm R U R' U' all centerpieces have the same state they occupied prior to the algorithm's execution, while most of the edges and corners in the R and U layers will have been moved or rotated. -However, in the reverse case, knowledge of the applied move sequence is sufficient information to determine the exact state of all cubies. -Section \ref{subsec:tracking-face-turns} will explain how extract full move sequences using only the state information of the centers. +The state of a centerpiece is defined as its current rotational +position. Implicit in this definition is the fact that a centerpiece is +guaranteed to occupy one and only one of its four possible states at +any given time. + +Each of the six centerpieces has an independent set of four possible +states, yielding a total of 24 different centerpiece states for the +Rubik's Cube, of which there will always be exactly six active at any +given time. + +It's important to note that a knowledge of the current state of each +centerpiece does not imply knowledge of the exact state of each of edge +and corner cubie. For example, after applying the algorithm R U R' U' +all centerpieces have the same state they occupied prior to the +algorithm's execution, while most of the edges and corners in the R and +U layers will have been moved or rotated. However, in the reverse case, +knowledge of the applied move sequence is sufficient information to +determine the exact state of all cubies. Section +\ref{subsec:tracking-face-turns} will explain how extract full move +sequences using only the state information of the centers. \subsection{Tracking Face Turns} \label{subsec:tracking-face-turns} -The only way to change a centerpiece's state is by applying a face turn. -As such, when a centerpiece is rotated to a new position, its state changes to that of the new position. -From there, a simple comparison of the new state to the previous state reveals the exact face turn applied to the cube. -Thus, in order to determine the sequences of moves applied to the Rubik's Cube, one simply needs to track how the state of its centerpieces changes over time. +The only way to change a centerpiece's state is by applying a face +turn. As such, when a centerpiece is rotated to a new position, its +state changes to that of the new position. From there, a simple +comparison of the new state to the previous state reveals the exact +face turn applied to the cube. + +Thus, in order to determine the sequences of moves applied to the +Rubik's Cube, one simply needs to track how the state of its +centerpieces changes over time. \subsection{Conveying State Through Sound} \label{subsec:conveying-state-through-sound} -Conveying the current state of the cube's centerpieces through sound can be done by simply associating each of the 24 possible centerpiece states with a specific tone as shown in Table \ref{table:centerpiece-frequencies} and broadcasting a signal composed of the six tones corresponding to the active states of the cube's centerpieces. - -Whenever a face turn is applied, the tone associated with the rotated centerpiece would change to the tone representing the piece's new state. -From there, a microphone equipped device can record the broadcast frequencies, measure the changes in the frequencies over time, convert the frequency changes to state changes, and finally extract any move sequence applied to the cube. -Since there will only ever be a single tone broadcast for a specific face, the four tones used to represent each of its possible states can be chosen closer to the minimum separation specified in Section \ref{subsec:tone-distinctiveness}. -However, the gap between tones for different faces should be larger to help prevent incorrectly classifying a tone as conveying a state of a different face. -In the sample frequencies shown in Table \ref{table:centerpiece-frequencies}, the frequency gap between faces is 200Hz, a value which performed well in testing. +Conveying the current state of the cube's centerpieces through sound +can be done by simply associating each of the 24 possible centerpiece +states with a specific tone as shown in Table +\ref{table:centerpiece-frequencies} and broadcasting a signal composed +of the six tones corresponding to the active states of the cube's +centerpieces. + +Whenever a face turn is applied, the tone associated with the rotated +centerpiece would change to the tone representing the piece's new +state. From there, a microphone equipped device can record the +broadcast frequencies, measure the changes in the frequencies over +time, convert the frequency changes to state changes, and finally +extract any move sequence applied to the cube. + +Since there will only ever be a single tone broadcast for a specific +face, the four tones used to represent each of its possible states can +be chosen closer to the minimum separation specified in Section +\ref{subsec:tone-distinctiveness}. However, the gap between tones for +different faces should be larger to help prevent incorrectly +classifying a tone as conveying a state of a different face. In the +sample frequencies shown in Table \ref{table:centerpiece-frequencies}, +the frequency gap between faces is 200Hz, a value which performed well +in testing. \begin{table}[h] \centering @@ -264,32 +392,72 @@ In the sample frequencies shown in Table \ref{table:centerpiece-frequencies}, th \label{sec:alternatives} % \subsection{Relative Sound Positioning} % \label{subsec:relative-sound-positioning} -Instead of transmitting the current state of the cube's centerpieces, an alternative protocol design could seek to directly transmit the face turns applied to the cube. - -This perspective focuses on the fact that all move sequences can be broken down into a series of 90$^\circ$ face turns. -Since the cube consists of 6 faces, and each face can be turned either clockwise or counterclockwise, one could design a two-tone protocol using only 8 discrete audio frequencies to build the smart cube. -The first tone would come from one of six predefined audio bands, one for each face of the cube. -The second tone would come from one of two separately predefined audio bands, one for each possible direction of rotation. -From this, an audio processing model could be designed to process a sequence of these two-tone pairs and reconstruct the sequence of face rotations by recording the rotated face followed by its direction of rotation. -However, while this model minimizes the number of discrete frequencies required to communicate changes in the cube's state, it carries many challenges. -Consider the example of a speedcuber averaging 5-8 turns per second (TPS) with bursts up to 20 TPS (common for a speedcuber that averages under 15 seconds per solution). -The burst TPS would require the successful transmission of 40 sequential tones within a single second - only 25ms per tone, all in the midst of additional noise from the cube's pieces hitting each other harder at the higher turn speed. -Adding to the difficulty, since each tone is only ever transmitted once, the audio detection model must achieve 100\% tone recognition to be able to accurately reconstruct the originating move sequence. -As a result, this model fails to support any sort of error correction that would make it resistant to the common challenges to data transmission through sound. +Instead of transmitting the current state of the cube's centerpieces, +an alternative protocol design could seek to directly transmit the face +turns applied to the cube. + +This perspective focuses on the fact that all move sequences can be +broken down into a series of 90$^\circ$ face turns. Since the cube +consists of 6 faces, and each face can be turned either clockwise or +counterclockwise, one could design a two-tone protocol using only 8 +discrete audio frequencies to build the smart cube. The first tone +would come from one of six predefined audio bands, one for each face of +the cube. The second tone would come from one of two separately +predefined audio bands, one for each possible direction of rotation. +From this, an audio processing model could be designed to process a +sequence of these two-tone pairs and reconstruct the sequence of face +rotations by recording the rotated face followed by its direction of +rotation. + +However, while this model minimizes the number of discrete frequencies +required to communicate changes in the cube's state, it carries many +challenges. Consider the example of a speedcuber averaging 5-8 turns +per second (TPS) with bursts up to 20 TPS (common for a speedcuber that +averages under 15 seconds per solution). The burst TPS would require +the successful transmission of 40 sequential tones within a single +second - only 25ms per tone, all in the midst of additional noise from +the cube's pieces hitting each other harder at the higher turn speed. +Adding to the difficulty, since each tone is only ever transmitted +once, the audio detection model must achieve 100\% tone recognition to +be able to accurately reconstruct the originating move sequence. As a +result, this model fails to support any sort of error correction that +would make it resistant to the common challenges to data transmission +through sound. \newpage \section{Summary} \label{sec:protocol-summary} -In summary, a sound based protocol for tracking the moves of a Rubik's Cube must be considerate of the following constraints: + +In summary, a sound based protocol for tracking the moves of a Rubik's +Cube must be considerate of the following constraints: \begin{enumerate} - \item Tones should be transmitted with a strength greater than -30dB to achieve a high signal-to-noise ratio. (Section \ref{subsec:signal-to-noise-ratio}) - \item Tones should be separated by at least 100Hz in order to be clearly distinguished from other tones in the protocol. (Section \ref{subsec:tone-distinctiveness}) - \item Tones must fall on the range of 20Hz-20kHz in order to be detectable by a typical smartphone or laptop microphone. (Section \ref{subsec:frequency-response-range}) - \item Tones should fall on the range 0Hz-4kHz or 16kHz-20kHz to minimize annoyance to the human speedsolver. (Section \ref{subsec:human-auditory-range}). + + \item Tones should be transmitted with a strength greater than + -30dB to achieve a high signal-to-noise ratio. (Section + \ref{subsec:signal-to-noise-ratio}) + + \item Tones should be separated by at least 100Hz in order to be + clearly distinguished from other tones in the protocol. (Section + \ref{subsec:tone-distinctiveness}) + + \item Tones must fall on the range of 20Hz-20kHz in order to be + detectable by a typical smartphone or laptop microphone. (Section + \ref{subsec:frequency-response-range}) + + \item Tones should fall on the range 0Hz-4kHz or 16kHz-20kHz to + minimize annoyance to the human speedsolver. (Section + \ref{subsec:human-auditory-range}). + \end{enumerate} -The protocol that will be used in subsequent chapters consists of 24 unique tones, one for each possible rotational state of each centerpiece. -A transmitter embedded in the Rubik's Cube will continuously broadcast the six tones corresponding to the current rotational state of each centerpiece adjust those tones as face turns are applied (Details in Chapter \ref{Chapter6}). -A software receiver will then listen to the transmitted audio and measure the changes in the transmitted frequencies over time to reconstruct the originally applied sequence of face turns (Details in Chapter \ref{Chapter5}). +The protocol that will be used in subsequent chapters consists of 24 +unique tones, one for each possible rotational state of each +centerpiece. A transmitter embedded in the Rubik's Cube will +continuously broadcast the six tones corresponding to the current +rotational state of each centerpiece adjust those tones as face turns +are applied (Details in Chapter \ref{Chapter6}). A software receiver +will then listen to the transmitted audio and measure the changes in +the transmitted frequencies over time to reconstruct the originally +applied sequence of face turns (Details in Chapter \ref{Chapter5}). diff --git a/thesis/draft/Chapters/Chapter5-Algorithm-Design.tex b/thesis/draft/Chapters/Chapter5-Algorithm-Design.tex index 86c332e..75807ed 100644 --- a/thesis/draft/Chapters/Chapter5-Algorithm-Design.tex +++ b/thesis/draft/Chapters/Chapter5-Algorithm-Design.tex @@ -5,37 +5,82 @@ \label{Chapter5} \section{Introduction} -The receiver for the sound-based move tracking protocol is in charge of decoding a sequence of face turns from the transmitted audio. + +The receiver for the sound-based move tracking protocol is in charge of +decoding a sequence of face turns from the transmitted audio. To do this, the receiver must go through the following steps: + \begin{enumerate} - \item Record the transmitted audio. + + \item Record the transmitted audio.q + \item Measure the audible frequencies at each time step. - \item Convert each time step's audible frequencies to the cube's state at that moment. - \item Decode the applied face turns from the sequence of cube states. + + \item Convert each time step's audible frequencies to the cube's + state at that moment. + + \item Decode the applied face turns from the sequence of cube + states. + \end{enumerate} -This chapter will describe the development of a software algorithm in Python that can serve as a receiver for this sound-based move tracking protocol. -This development began with the creation of synthetic audio recordings representing the tones that would be emitted by a Rubik's Cube equipped with an ideal transmitter (Section \ref{sec:synthetic-audio-generation}) followed by the implementation of an algorithm capable of decoding that ideal synthetic audio (Section \ref{sec:decoding-synthetic-audio}). -The algorithm was then made more robust by adding realistic noise to the synthetic audio to better simulate a real speedcubing environment (Section \ref{sec:adding-realistic-noise}) followed by enhancing the previously designed algorithm to continue to decode the applied move sequence in the midst of the added noise (Section: \ref{sec:decoding-realistic-noise}). \footnote{The contents of this chapter have been specially written so that the reader can copy them into a Jupyter Notebook running a Python 3.9 kernel and see the same results for himself/herself.} +This chapter will describe the development of a software algorithm in +Python that can serve as a receiver for this sound-based move tracking +protocol. This development began with the creation of synthetic audio +recordings representing the tones that would be emitted by a Rubik's +Cube equipped with an ideal transmitter (Section +\ref{sec:synthetic-audio-generation}) followed by the implementation of +an algorithm capable of decoding that ideal synthetic audio (Section +\ref{sec:decoding-synthetic-audio}). The algorithm was then made more +robust by adding realistic noise to the synthetic audio to better +simulate a real speedcubing environment (Section +\ref{sec:adding-realistic-noise}) followed by enhancing the previously +designed algorithm to continue to decode the applied move sequence in +the midst of the added noise (Section: +\ref{sec:decoding-realistic-noise}). \footnote{The contents of this +chapter have been specially written so that the reader can copy them +into a Jupyter Notebook running a Python 3.9 kernel and see the same +results for himself/herself.} \section{Creating Synthetic Audio} \label{sec:synthetic-audio-generation} -The first step of designing this receiver is to synthesize an audio signal representative of the output of the ideal transmitter. -This audio synthesis starts with encoding the frequency corresponding to each centerpiece state (Section \ref{subsec:represent-audio-protocol}), then involves creating a virtual Rubik's Cube (Section \ref{subsec:represent-rubiks-cube}), and finally culminates in generating the audio signal from the virtual Rubik's Cube's state (Section \ref{subsec:generate-audible-algorithm}). +The first step of designing this receiver is to synthesize an audio +signal representative of the output of the ideal transmitter. + +This audio synthesis starts with encoding the frequency corresponding +to each centerpiece state (Section +\ref{subsec:represent-audio-protocol}), then involves creating a +virtual Rubik's Cube (Section \ref{subsec:represent-rubiks-cube}), and +finally culminates in generating the audio signal from the virtual +Rubik's Cube's state (Section \ref{subsec:generate-audible-algorithm}). + \newpage \subsection{Representing the Audio Protocol} \label{subsec:represent-audio-protocol} -For the synthetic audio generator to produce a realistic signal, it needs to know which frequencies to transmit for each centerpiece state. -This is easily accomplished by creating a dictionary to map each possible state of each face to its corresponding frequency. -For this design, the frequency assignments from Table \ref{table:centerpiece-frequencies} are converted to the dictionary shown in Figure \ref{fig:code-freq-mapping-dict} with two small changes. -First, by assuming no cube rotations, the face color (e.g. "White") can be converted to a layer name (e.g. "U") which simplifies the code for both encoding and decoding a move sequence from audio. -Second, the 0$^\circ$, 90$^\circ$, 180$^\circ$, and 270$^\circ$ alignments from Figure \ref{fig:rotation-alignment} are divided by 90 to be represented by the simple sequence of consecutive integers 0, 1, 2, and 3 that are easier to manipulate in code. - -With this dictionary in place, the frequency to transmit for any particular centerpiece's current rotation can be determined by looking up the centerpiece and its rotation in the dictionary as shown in Figure \ref{fig:code-frequency-of}. +For the synthetic audio generator to produce a realistic signal, it +needs to know which frequencies to transmit for each centerpiece state. +This is easily accomplished by creating a dictionary to map each +possible state of each face to its corresponding frequency. + +For this design, the frequency assignments from Table +\ref{table:centerpiece-frequencies} are converted to the dictionary +shown in Figure \ref{fig:code-freq-mapping-dict} with two small +changes. First, by assuming no cube rotations, the face color (e.g. +"White") can be converted to a layer name (e.g. "U") which simplifies +the code for both encoding and decoding a move sequence from audio. +Second, the 0$^\circ$, 90$^\circ$, 180$^\circ$, and 270$^\circ$ +alignments from Figure \ref{fig:rotation-alignment} are divided by 90 +to be represented by the simple sequence of consecutive integers 0, 1, +2, and 3 that are easier to manipulate in code. + +With this dictionary in place, the frequency to transmit for any +particular centerpiece's current rotation can be determined by looking +up the centerpiece and its rotation in the dictionary as shown in +Figure \ref{fig:code-frequency-of}. \begin{figure}[h] \caption{Centerpiece State to Frequency Mapping} @@ -79,10 +124,22 @@ def frequency_of(centerpiece: str, rotation: int) -> float: \newpage \subsection{Representing the Rubik's Cube} \label{subsec:represent-rubiks-cube} -While there are many implementations of a digital Rubik's Cube that can track every cubie and render an interactive 3D cube, all that's needed for the synthetic audio generator is a representation of a Rubik's Cube on which individual face turns can be virtually applied and the resulting centerpiece state can be read out. -To do this, a \code{RubiksCube} object is created that encapsulates a dictionary containing the same keys for each face as the \code{FREQUENCY\_MAPPINGS} dictionary in Figure \ref{fig:code-freq-mapping-dict} above, each associated with a single integer representing the current rotational state of that face. -A method called \code{apply\_move} is also defined with a parameter for a valid move like \code{U} or \code{U'} that will update the \code{RubiksCube}'s state to reflect the rotation. +While there are many implementations of a digital Rubik's Cube that can +track every cubie and render an interactive 3D cube, all that's needed +for the synthetic audio generator is a representation of a Rubik's Cube +on which individual face turns can be virtually applied and the + +resulting centerpiece state can be read out. To do this, a +\code{RubiksCube} object is created that encapsulates a dictionary +containing the same keys for each face as the +\code{FREQUENCY\_MAPPINGS} dictionary in Figure +\ref{fig:code-freq-mapping-dict} above, each associated with a single +integer representing the current rotational state of that face. A +method called \code{apply\_move} is also defined with a parameter for a +valid move like \code{U} or \code{U'} that will update the +\code{RubiksCube}'s state to reflect the rotation. + \begin{figure}[h] \caption{A simple abstraction of a Rubik's Cube} \label{fig:rubiks-cube-code} @@ -113,16 +170,29 @@ class RubiksCube: \subsection{Creating Synthetic Audio for an Arbitrary Algorithm} \label{subsec:generate-audible-algorithm} -Now the synthetic audio can be generated for any valid algorithm. -This is done using the \code{tones} library created by Erik Nyquist. \cite{pip-tones} -First, a separate audio track is created to simulate the output of each speaker embedded into each centerpiece on the cube (Figure \ref{fig:code-create-mixer}). - -Second, a new function is written to add the tones corresponding to the virtual cube's current state to the audio mixer using the \code{frequency\_of} function from Figure \ref{fig:code-frequency-of} (Figure \ref{fig:code-render-cube-state}). -This function includes a \code{tps} parameter that controls the duration of the added tones so that they change to the next tone at the same speed as the turns on a speedcube being solved at that number of turns per second. - -Then, those two functions are tied together by iterating through each move in a Rubik's Cube \code{alg}orithm and saving the audio of the resulting state changes into a .wav file at a given file path (Figure \ref{fig:code-render-audible-alg}). -Additionally, the \code{tps} parameter is propagated to this function to enable generating audio for an algorithm at different turn speeds. +Now the synthetic audio can be generated for any valid algorithm. This +is done using the \code{tones} library created by Erik Nyquist. +\cite{pip-tones} + +First, a separate audio track is created to simulate the output of each +speaker embedded into each centerpiece on the cube (Figure +\ref{fig:code-create-mixer}). + +Second, a new function is written to add the tones corresponding to the +virtual cube's current state to the audio mixer using the +\code{frequency\_of} function from Figure \ref{fig:code-frequency-of} +(Figure \ref{fig:code-render-cube-state}). This function includes a +\code{tps} parameter that controls the duration of the added tones so +that they change to the next tone at the same speed as the turns on a +speedcube being solved at that number of turns per second. + +Then, those two functions are tied together by iterating through each +move in a Rubik's Cube \code{alg}orithm and saving the audio of the +resulting state changes into a .wav file at a given file path (Figure +\ref{fig:code-render-audible-alg}). Additionally, the \code{tps} +parameter is propagated to this function to enable generating audio for +an algorithm at different turn speeds. \begin{figure}[h] \caption{Generating audio for any Rubik's Cube algorithm} @@ -175,8 +245,10 @@ def render_audible_alg(alg: str, wav_path: str=None, tps: float=4): \end{subfigure} \end{figure} -With these functions in place, synthetic audio can be easily created for any valid Rubik's Cube algorithm. -For example, generating synthetic audio that sweeps through every possible centerpiece state can be done with the two lines of code in Figure \ref{fig:example-alg-audio}. +With these functions in place, synthetic audio can be easily created +for any valid Rubik's Cube algorithm. For example, generating synthetic +audio that sweeps through every possible centerpiece state can be done +with the two lines of code in Figure \ref{fig:example-alg-audio}. \begin{figure}[h] \caption{Example Audio Generation for a Rubik's Cube Algorithm} @@ -190,14 +262,30 @@ render_audible_alg(demo_alg, "demo_all_states.wav") \section{Decoding the Synthetic Audio} \label{sec:decoding-synthetic-audio} -With synthetic audio now available for any valid Rubik's Cube algorithm, the next step is to create an initial software algorithm that can decode that audio back into the original move sequence. -Accomplishing this will require computing the synthetic audio's spectrogram (Section \ref{subsec:compute-spectrogram}), followed by extracting the dominant frequencies present at each time step (Section \ref{subsec:extract-dominant-freqs}), and converting those dominant frequencies into the corresponding Rubik's Cube centerpiece states (Section \ref{subsec:translating-freqs-to-state}), all before finally recovering the originally applied move sequence (Section \ref{subsec:extract-moves}). +With synthetic audio now available for any valid Rubik's Cube +algorithm, the next step is to create an initial software algorithm +that can decode that audio back into the original move sequence. + +Accomplishing this will require computing the synthetic audio's +spectrogram (Section \ref{subsec:compute-spectrogram}), followed by +extracting the dominant frequencies present at each time step (Section +\ref{subsec:extract-dominant-freqs}), and converting those dominant +frequencies into the corresponding Rubik's Cube centerpiece states +(Section \ref{subsec:translating-freqs-to-state}), all before finally +recovering the originally applied move sequence (Section +\ref{subsec:extract-moves}). \subsection{Computing the Spectrogram} \label{subsec:compute-spectrogram} -The first step in decoding the synthetic audio is determining its component frequencies at any specific moment in time. -These component frequencies can be easily visualized using a spectrogram, like the one in Figure \ref{fig:spectrogram} of the synthetic audio created in Section \ref{subsec:generate-audible-algorithm}. \footnote{The source code for Figure \ref{fig:spectrogram} is available in Appendix \ref{sec:code-spectrogram}.} + +The first step in decoding the synthetic audio is determining its +component frequencies at any specific moment in time. These component +frequencies can be easily visualized using a spectrogram, like the one +in Figure \ref{fig:spectrogram} of the synthetic audio created in +Section \ref{subsec:generate-audible-algorithm}. \footnote{The source +code for Figure \ref{fig:spectrogram} is available in Appendix +\ref{sec:code-spectrogram}.} \begin{figure}[h] \centering @@ -206,27 +294,66 @@ These component frequencies can be easily visualized using a spectrogram, like t \includegraphics[width=0.8\textwidth]{Figures/5 Algorithm Design/component_frequencies.png} \end{figure} -This spectrogram has the same pieces as the ones described in Section \ref{subsubsec:how-to-read-a-spectrogram} with a few minor changes. -First the order of the graphs has been reversed, so the top graph shows the actual spectrogram and the bottom graph shows the strength of the component frequencies at the specific point in time indicated by the vertical red line on the top graph. -Additionally, the spectrogram has been rotated 90$^\circ$ so time is now on the x-axis and the component frequencies are shown on the y-axis. -Finally, the color scheme is slightly different, with the presence of a strong component frequency indicated by a bright yellow color instead of bright purple or pink. +This spectrogram has the same pieces as the ones described in Section +\ref{subsubsec:how-to-read-a-spectrogram} with a few minor changes. +First the order of the graphs has been reversed, so the top graph shows +the actual spectrogram and the bottom graph shows the strength of the +component frequencies at the specific point in time indicated by the +vertical red line on the top graph. Additionally, the spectrogram has +been rotated 90$^\circ$ so time is now on the x-axis and the component +frequencies are shown on the y-axis. Finally, the color scheme is +slightly different, with the presence of a strong component frequency +indicated by a bright yellow color instead of bright purple or pink. \subsubsection{The Brief Overview of the Math behind the Spectrogram} -However, while Matplotlib's \code{specgram} plot \cite{matplotlib} made it easy to create Figure \ref{fig:spectrogram}, it didn't return the underlying data of the spectrogram required to decode the exact values and strengths of the component frequencies at each point in time. -To compute that data directly, it is helpful to understand the basics of the math behind the spectrogram. Dr. Steve Brunton from the University of Washington created an excellent video series on this topic \cite{fourier-analysis}, of which the following few paragraphs are a short summary. - -The foundational mathematical concept behind the spectrogram is the Fourier Transform, which is a technique for approximating any continuous function using only sine and cosine functions. -In the case of an analog audio signal -which is inherently a composite of many sine functions (one for each component frequency)- applying a Fourier Transform would return data for a graph of all the frequencies present at any point in the entire signal and their overall strength throughout it. - -But digital audio signals like .wav files are a series of discrete values, not continuous functions, which means the Fourier Transform cannot directly operate on them. -Fortunately, there exists a variant of the Fourier Transform called the Discrete Fourier Transform which can operate on a list of discrete data points by assuming they sample a continuous function and then computing the Fourier Transform of that function. - -However, when applied to a digital audio signal, the Discrete Fourier Transform still only computes which frequencies were present at \emph{any} time during the duration of the signal, but not \emph{when} they occurred. -That data comes from another layer of computation called the Gabor Transform (i.e. the Short-Time Fourier Transform) which computes a Discrete Fourier Transform over a sliding window of the input samples to approximate the changes in the strength of each component frequency over time. -This knowledge, combined with the \code{numpy} \cite{numpy} and \code{scipy} \cite{scipy} libraries which can calculate these transforms, enables the creation of a function (Figure \ref{fig:code-compute-spectrogram}) to compute the spectrogram for the audio stored in a given .wav file. -The return values \code{freq} and \code{time} are, respectively, lists of the .wav file's component frequencies and time steps, while \code{spectrogram} is a 2D array of the strengths of each component frequency at each time step. -They are related by common indices, such that the strength of the frequency \code{freq[f\_idx]} at the time \code{time[t\_idx]} is found in \code{spectrogram[t\_idx][f\_idx]}. +However, while Matplotlib's \code{specgram} plot \cite{matplotlib} made +it easy to create Figure \ref{fig:spectrogram}, it didn't return the +underlying data of the spectrogram required to decode the exact values +and strengths of the component frequencies at each point in time. To +compute that data directly, it is helpful to understand the basics of +the math behind the spectrogram. Dr. Steve Brunton from the University +of Washington created an excellent video series on this topic +\cite{fourier-analysis}, of which the following few paragraphs are a +short summary. + +The foundational mathematical concept behind the spectrogram is the +Fourier Transform, which is a technique for approximating any +continuous function using only sine and cosine functions. In the case +of an analog audio signal -which is inherently a composite of many sine +functions (one for each component frequency)- applying a Fourier +Transform would return data for a graph of all the frequencies present +at any point in the entire signal and their overall strength throughout +it. + +But digital audio signals like .wav files are a series of discrete +values, not continuous functions, which means the Fourier Transform +cannot directly operate on them. Fortunately, there exists a variant of +the Fourier Transform called the Discrete Fourier Transform which can +operate on a list of discrete data points by assuming they sample a +continuous function and then computing the Fourier Transform of that +function. + +However, when applied to a digital audio signal, the Discrete Fourier +Transform still only computes which frequencies were present at +\emph{any} time during the duration of the signal, but not \emph{when} +they occurred. That data comes from another layer of computation called +the Gabor Transform (i.e. the Short-Time Fourier Transform) which +computes a Discrete Fourier Transform over a sliding window of the +input samples to approximate the changes in the strength of each +component frequency over time. + +This knowledge, combined with the \code{numpy} \cite{numpy} and +\code{scipy} \cite{scipy} libraries which can calculate these +transforms, enables the creation of a function (Figure +\ref{fig:code-compute-spectrogram}) to compute the spectrogram for the +audio stored in a given .wav file. The return values \code{freq} and +\code{time} are, respectively, lists of the .wav file's component +frequencies and time steps, while \code{spectrogram} is a 2D array of +the strengths of each component frequency at each time step. They are +related by common indices, such that the strength of the frequency +\code{freq[f\_idx]} at the time \code{time[t\_idx]} is found in +\code{spectrogram[t\_idx][f\_idx]}. \begin{figure}[h] \caption{Function to compute the spectrogram of a .wav file} @@ -248,10 +375,21 @@ def compute_spectrogram(wav_path: str): \subsection{Extracting the Dominant Component Frequencies} \label{subsec:extract-dominant-freqs} -Looking back at the component frequency graph in Figure \ref{fig:spectrogram} it is clear that it has six distinct peaks: these are the transmitted frequencies representing the current state of the virtual Rubik's Cube's six centerpieces at that specific instant of time. -The exact frequencies of these peaks can be extracted by filtering out all frequencies whose strength is not above a specific threshold. -In this case, a simple threshold of 85\% of the maximum strength of any component frequency (see the green line in Figure \ref{fig:spectrogram-with-naive-threshold}\footnote{The source code for Figure \ref{fig:spectrogram-with-naive-threshold} is available in Appendix \ref{sec:code-spectrogram-with-naive-threshold}.}) isolates the six dominant component frequencies. +Looking back at the component frequency graph in Figure +\ref{fig:spectrogram} it is clear that it has six distinct peaks: these +are the transmitted frequencies representing the current state of the +virtual Rubik's Cube's six centerpieces at that specific instant of +time. + +The exact frequencies of these peaks can be extracted by filtering out +all frequencies whose strength is not above a specific threshold. In +this case, a simple threshold of 85\% of the maximum strength of any +component frequency (see the green line in Figure +\ref{fig:spectrogram-with-naive-threshold}\footnote{The source code for +Figure \ref{fig:spectrogram-with-naive-threshold} is available in +Appendix \ref{sec:code-spectrogram-with-naive-threshold}.}) isolates +the six dominant component frequencies. \begin{figure}[h] \centering @@ -260,9 +398,14 @@ In this case, a simple threshold of 85\% of the maximum strength of any componen \includegraphics[width=0.8\textwidth]{Figures/5 Algorithm Design/threshold.png} \end{figure} -Using the actual spectrogram data, the exact values of these peaks can be computed. -First, the threshold computation is broken into its own function (Figure \ref{fig:code-compute-threshold}). -From there, a second function (Figure \ref{fig:code-extract-important-freqs}) can iterate through all the component frequencies of the computed spectrogram data at a specific time index to compile and return a list of the frequencies whose strength exceeds the threshold at that time step. +Using the actual spectrogram data, the exact values of these peaks can +be computed. First, the threshold computation is broken into its own +function (Figure \ref{fig:code-compute-threshold}). From there, a +second function (Figure \ref{fig:code-extract-important-freqs}) can +iterate through all the component frequencies of the computed +spectrogram data at a specific time index to compile and return a list +of the frequencies whose strength exceeds the threshold at that time +step. \begin{figure}[h] \caption{Extracting dominant frequencies from one time step of audio} @@ -301,7 +444,10 @@ def extract_important_freqs(freq, time, spectrogram, t_idx): \end{subfigure}\\ \end{figure} -With these functions, finding the actual frequencies of the peaks in Figure \ref{fig:spectrogram-with-naive-threshold} can be done in just a few lines of code as shown in Figure \ref{fig:code-extract-important-freqs-demo}. +With these functions, finding the actual frequencies of the peaks in +Figure \ref{fig:spectrogram-with-naive-threshold} can be done in just a +few lines of code as shown in Figure +\ref{fig:code-extract-important-freqs-demo}. \begin{figure}[h] \caption{Example peak frequency decoding at a specific time step} @@ -324,10 +470,21 @@ print([f"{x["hz"]:.0f}Hz" for x in important_freqs]) \subsection{Translating Component Frequencies to Centerpiece States} \label{subsec:translating-freqs-to-state} -With the specific frequencies of each detected peak, the original state of the Rubik's Cube at that moment in time can be computed by finding the states whose corresponding frequency is closest to each detected peak frequency. -This is done by first defining a function that will find the closest centerpiece state for one peak frequency by iterating through every centerpiece state in the \code{FREQUENCY\_MAPPINGS} dictionary from Section \ref{subsec:represent-audio-protocol}, comparing the difference between the state's frequency and the given peak frequency, and returning the state with the smallest difference (Figure \ref{fig:code-closest-state}). -This function can then be called for all the detected peak frequencies to recover the state of the cube at that time step (Figure \ref{fig:code-get-state-from-freqs}). +With the specific frequencies of each detected peak, the original state +of the Rubik's Cube at that moment in time can be computed by finding +the states whose corresponding frequency is closest to each detected +peak frequency. + +This is done by first defining a function that will find the closest +centerpiece state for one peak frequency by iterating through every +centerpiece state in the \code{FREQUENCY\_MAPPINGS} dictionary from +Section \ref{subsec:represent-audio-protocol}, comparing the difference +between the state's frequency and the given peak frequency, and +returning the state with the smallest difference (Figure +\ref{fig:code-closest-state}). This function can then be called for all +the detected peak frequencies to recover the state of the cube at that +time step (Figure \ref{fig:code-get-state-from-freqs}). \begin{figure}[h] \caption{Converting peak frequencies to centerpiece states} @@ -368,7 +525,10 @@ def get_state_from_freqs(important_freqs: list) -> dict: \end{subfigure} \end{figure} -For example, Figure \ref{fig:code-get-state-from-freqs-demo} shows how using these functions makes it easy to covert the peak frequencies extracted in Figure \ref{fig:code-extract-important-freqs-demo} to their corresponding centerpiece states. +For example, Figure \ref{fig:code-get-state-from-freqs-demo} shows how +using these functions makes it easy to covert the peak frequencies +extracted in Figure \ref{fig:code-extract-important-freqs-demo} to +their corresponding centerpiece states. \begin{figure}[h] \caption{Example conversion of peak frequencies to states} @@ -387,8 +547,11 @@ print(detected_state) \end{subfigure} \end{figure} -As such, obtaining a sequence of the Rubik's Cube's centerpiece states over the course of the recorded audio sequence only requires repeating this process for each time step in the spectrogram data. -This can also be easily turned into another function as shown in Figure \ref{fig:code-get-state-over-time}. +As such, obtaining a sequence of the Rubik's Cube's centerpiece states +over the course of the recorded audio sequence only requires repeating +this process for each time step in the spectrogram data. This can also +be easily turned into another function as shown in Figure +\ref{fig:code-get-state-over-time}. \begin{figure}[h] \caption{Function to list the cube's state at each time step} @@ -413,7 +576,11 @@ def get_state_over_time(freq, time, spectrogram): \end{lstlisting} \end{figure} -And, for completeness, Figure \ref{fig:code-get-state-over-time-demo} shows an example of using that function to get the full sequence of states for the synthetic audio generated in Section \ref{sec:synthetic-audio-generation}. +And, for completeness, Figure \ref{fig:code-get-state-over-time-demo} +shows an example of using that function to get the full sequence of +states for the synthetic audio generated in Section +\ref{sec:synthetic-audio-generation}. + \begin{figure}[h] \caption{Example listing of states over time} \label{fig:code-get-state-over-time-demo} @@ -436,12 +603,23 @@ print(state_over_time) \newpage \subsection{Extracting Move Sequences from Centerpiece State Sequences} \label{subsec:extract-moves} -The final step to recover the original move sequence is to iterate over the sequence of cube states and register any change to the cube state as a move applied to the cube. -This starts with a function (Figure \ref{fig:code-move-from}) that, given a starting and ending rotational state for a specific face, can calculate which direction a face was turned and return the text notation of the applied face turn. -A second function (Figure \ref{fig:code-detect-moves}) then iterates through the state sequence extracted in Section \ref{subsec:translating-freqs-to-state} checking each state in the sequence against the previous one to detect when the state changes. -Upon detecting a change, this second function then calls the first to get the actual move that caused the state change and saves it to a list of \code{detected\_moves} to be returned after iterating through all states. -This list of \code{detected\_moves} is the final move sequence that was extracted from the synthetic audio. +The final step to recover the original move sequence is to iterate over +the sequence of cube states and register any change to the cube state +as a move applied to the cube. + +This starts with a function (Figure \ref{fig:code-move-from}) that, +given a starting and ending rotational state for a specific face, can +calculate which direction a face was turned and return the text +notation of the applied face turn. A second function (Figure +\ref{fig:code-detect-moves}) then iterates through the state sequence +extracted in Section \ref{subsec:translating-freqs-to-state} checking +each state in the sequence against the previous one to detect when the +state changes. Upon detecting a change, this second function then calls +the first to get the actual move that caused the state change and saves +it to a list of \code{detected\_moves} to be returned after iterating +through all states. This list of \code{detected\_moves} is the final +move sequence that was extracted from the synthetic audio. \begin{figure}[h] @@ -494,8 +672,11 @@ def detect_moves(state_over_time): \end{figure} \newpage -Figure \ref{fig:code-detect-moves-demo} shows an example usage of the \code{detect\_moves} function. -Additionally, the extracted Rubik's Cube algorithm is compared to the original one used to create the synthetic audio. + +Figure \ref{fig:code-detect-moves-demo} shows an example usage of the +\code{detect\_moves} function. Additionally, the extracted Rubik's Cube +algorithm is compared to the original one used to create the synthetic +audio. \begin{figure}[h] \caption{Example move sequence extraction} @@ -518,11 +699,19 @@ print(f"Matches demo_alg? {demo_alg == pretty_moves}") \end{subfigure} \end{figure} -Clearly, the detected move sequence matches the demo algorithm used to create the synthetic audio in Section \ref{subsec:generate-audible-algorithm}, which means this algorithm is a functional receiver for an ideal transmitter. +Clearly, the detected move sequence matches the demo algorithm used to +create the synthetic audio in Section +\ref{subsec:generate-audible-algorithm}, which means this algorithm is +a functional receiver for an ideal transmitter. \subsection{Full Example: Extracting Moves from Synthetic Audio} -As a summary of this section on decoding synthetic audio, Figure \ref{fig:code-synthetic-extraction-demo} shows the full process for creating a synthetic audio clip and extracting the encoded move sequence from it. -For this example, the chosen move sequence to transmit consists of every possible face turn, which will test that this strategy can correctly report back each face turn. + +As a summary of this section on decoding synthetic audio, Figure +\ref{fig:code-synthetic-extraction-demo} shows the full process for +creating a synthetic audio clip and extracting the encoded move +sequence from it. For this example, the chosen move sequence to +transmit consists of every possible face turn, which will test that +this strategy can correctly report back each face turn. \begin{figure}[h] \caption{Full Example: Audio generation and move extraction} @@ -554,20 +743,47 @@ print(f"Matches demo2_alg? {demo2_alg == pretty_moves2}") \end{subfigure} \end{figure} -And again, the extracted move sequence does match the one used to generate the synthetic audio, further validating this strategy for decoding moves from audio. +And again, the extracted move sequence does match the one used to +generate the synthetic audio, further validating this strategy for +decoding moves from audio. \newpage \section{Creating Realistic Audio} \label{sec:adding-realistic-noise} -While the algorithm created in Section \ref{sec:decoding-synthetic-audio} can successfully recover a sequence of moves applied to a virtual Rubik's Cube with 100\% accuracy, the synthetic audio that it operates on is not realistic. -This is made obvious by a comparison between the spectrograms of the background audio in Figure \ref{fig:signal-to-noise-ratio} and the synthetic audio in Figure \ref{fig:spectrogram}. -In the latter, the bright yellow indicators of present frequencies are very clear and strong with no other frequencies present in the signal. -In contrast, the former contains many areas with bright pink indicators of prominent frequencies (the color differences are due to the use of different applications to generate the diagrams). -A more realistic signal would contain both the strong bands of the transmitted frequency along with the underlying background noise of whatever cube is actively being solved. -Adding this noise could be done in software by overlaying the synthetic audio with a recording of the background noise, but it was just as easy to play the synthetic audio from a laptop speaker and record it using a nearby smartphone while solving various Rubik's Cubes. -An example of the spectrogram resulting from recording the audio of the demo algorithm from Section \ref{subsec:generate-audible-algorithm} on a Google Pixel smartphone while actively solving a Gans 356 speedcube can be seen in Figure \ref{fig:noisy-spectrogram}. -\footnote{The astute reader will notice that the audio bands in Figure \ref{fig:noisy-spectrogram} fall on slightly different frequencies than in Figure \ref{fig:spectrogram}. This is because the realistic audio samples used for analysis in Sections \ref{sec:adding-realistic-noise} and \ref{sec:decoding-realistic-noise} were created before finalizing the frequency assignments presented Table \ref{table:centerpiece-frequencies}. Ultimately, this change in frequencies shows that this algorithm works across a variety of encoding frequencies.} +While the algorithm created in Section +\ref{sec:decoding-synthetic-audio} can successfully recover a sequence +of moves applied to a virtual Rubik's Cube with 100\% accuracy, the +synthetic audio that it operates on is not realistic. This is made +obvious by a comparison between the spectrograms of the background +audio in Figure \ref{fig:signal-to-noise-ratio} and the synthetic audio +in Figure \ref{fig:spectrogram}. In the latter, the bright yellow +indicators of present frequencies are very clear and strong with no +other frequencies present in the signal. In contrast, the former +contains many areas with bright pink indicators of prominent +frequencies (the color differences are due to the use of different +applications to generate the diagrams). A more realistic signal would +contain both the strong bands of the transmitted frequency along with +the underlying background noise of whatever cube is actively being +solved. + +Adding this noise could be done in software by overlaying the synthetic +audio with a recording of the background noise, but it was just as easy +to play the synthetic audio from a laptop speaker and record it using a +nearby smartphone while solving various Rubik's Cubes. An example of +the spectrogram resulting from recording the audio of the demo +algorithm from Section \ref{subsec:generate-audible-algorithm} on a +Google Pixel smartphone while actively solving a Gans 356 speedcube can +be seen in Figure \ref{fig:noisy-spectrogram}. \footnote{The astute +reader will notice that the audio bands in Figure +\ref{fig:noisy-spectrogram} fall on slightly different frequencies than +in Figure \ref{fig:spectrogram}. This is because the realistic audio +samples used for analysis in Sections \ref{sec:adding-realistic-noise} +and \ref{sec:decoding-realistic-noise} were created before finalizing +the frequency assignments presented Table +\ref{table:centerpiece-frequencies}. Ultimately, this change in +frequencies shows that this algorithm works across a variety of +encoding frequencies.} \begin{figure}[h] \centering @@ -576,19 +792,35 @@ An example of the spectrogram resulting from recording the audio of the demo alg \includegraphics[width=0.8\linewidth]{Figures/5 Algorithm Design/transmitted-356-5tps.png} \end{figure} -Notice how the horizontal bands representing the transmitted signal are dimmer in the realistic audio than in the purely synthetic audio. -This reflects the loss of volume any tone experiences as it travels through the air. -Additionally, the transmitted signal is also partially obscured by the additional audible noise of solving the speedcube. +Notice how the horizontal bands representing the transmitted signal are +dimmer in the realistic audio than in the purely synthetic audio. This +reflects the loss of volume any tone experiences as it travels through +the air. Additionally, the transmitted signal is also partially +obscured by the additional audible noise of solving the speedcube. \section{Decoding Realistic Audio} \label{sec:decoding-realistic-noise} -The loss of signal strength alongside the added noise creates several unique challenges that require more sophisticated analysis than that presented in Section \ref{sec:decoding-synthetic-audio}. -These challenges include added variation in the strength of each peak frequency (Section \ref{subsec:fine-tuning-threshold}), the detection of conflicting centerpiece states (Section \ref{subsec:filtering-similar-peak-frequencies}), and the erroneous reading of background noise as centerpiece states (Section \ref{subsec:ignoring-noise-when-extracting-move-sequences}). + +The loss of signal strength alongside the added noise creates several +unique challenges that require more sophisticated analysis than that +presented in Section \ref{sec:decoding-synthetic-audio}. These +challenges include added variation in the strength of each peak +frequency (Section \ref{subsec:fine-tuning-threshold}), the detection +of conflicting centerpiece states (Section +\ref{subsec:filtering-similar-peak-frequencies}), and the erroneous +reading of background noise as centerpiece states (Section +\ref{subsec:ignoring-noise-when-extracting-move-sequences}). \subsection{Fine-Tuning the Threshold} \label{subsec:fine-tuning-threshold} -Because some audio frequencies are dampened more than others as they travel through the air to the recording microphone, a static threshold like the one used in Section \ref{subsec:extract-dominant-freqs} fails to capture all the dominant frequencies that compose the audible signal. -For example, Figure \ref{fig:threshold-miss} shows how the 85\% threshold from Section \ref{subsec:extract-dominant-freqs} misses five of the six signal peaks in the realistic audio. + +Because some audio frequencies are dampened more than others as they +travel through the air to the recording microphone, a static threshold +like the one used in Section \ref{subsec:extract-dominant-freqs} fails +to capture all the dominant frequencies that compose the audible +signal. For example, Figure \ref{fig:threshold-miss} shows how the 85\% +threshold from Section \ref{subsec:extract-dominant-freqs} misses five +of the six signal peaks in the realistic audio. \begin{figure}[h] \centering @@ -597,9 +829,16 @@ For example, Figure \ref{fig:threshold-miss} shows how the 85\% threshold from S \label{fig:threshold-miss} \end{figure} -Furthermore, the natural background noise can also cause false positives during the moments between moves while the signal is weaker as a result of changing from one state to the next. -For example, Figure \ref{fig:threshold-false-positives} shows a moment between face turns when the audio signal is nearly non-existent, and the background noise can falsely register as important peak frequencies for centerpiece state detection. -In this case, between 1000Hz and 2000Hz alone there are six different points where the background noise reaches the 85\% threshold compared to the three centerpieces who state gets transmitted within that same band. +Furthermore, the natural background noise can also cause false +positives during the moments between moves while the signal is weaker +as a result of changing from one state to the next. For example, Figure +\ref{fig:threshold-false-positives} shows a moment between face turns +when the audio signal is nearly non-existent, and the background noise +can falsely register as important peak frequencies for centerpiece +state detection. In this case, between 1000Hz and 2000Hz alone there +are six different points where the background noise reaches the 85\% +threshold compared to the three centerpieces who state gets transmitted +within that same band. \begin{figure}[h] \centering @@ -609,12 +848,24 @@ In this case, between 1000Hz and 2000Hz alone there are six different points whe \end{figure} \newpage -Resolving these issues starts by noticing that the power of each peak frequency bearing the audio signal is generally much higher than the power of all other component frequencies. -As such, basing the threshold at a power equal to one standard deviation of the power of the component frequencies of a specific time step generally captures all the peak frequencies while still excluding the underlying noise of the cube/environment. -Additionally, the use of a hard minimum value for the threshold helps reduce the number of false positives during the gaps in the signal between face turns. -These two changes are encoded in an updated version of the \code{compute\_threshold} function first defined in Section \ref{subsec:extract-dominant-freqs}. -This new version contains two new parameters: \code{stdv\_pct}, which can be used to adjust the threshold to some multiple of the standard deviation for testing purposes, and \code{min\_thresh} which is the "hard minimum value" discussed in the previous paragraph. +Resolving these issues starts by noticing that the power of each peak +frequency bearing the audio signal is generally much higher than the +power of all other component frequencies. As such, basing the threshold +at a power equal to one standard deviation of the power of the +component frequencies of a specific time step generally captures all +the peak frequencies while still excluding the underlying noise of the +cube/environment. Additionally, the use of a hard minimum value for the +threshold helps reduce the number of false positives during the gaps in +the signal between face turns. + +These two changes are encoded in an updated version of the +\code{compute\_threshold} function first defined in Section +\ref{subsec:extract-dominant-freqs}. This new version contains two new +parameters: \code{stdv\_pct}, which can be used to adjust the threshold +to some multiple of the standard deviation for testing purposes, and +\code{min\_thresh} which is the "hard minimum value" discussed in the +previous paragraph. \begin{figure}[h] \caption{Updated version of threshold computation in Figure \ref{fig:code-compute-threshold}} @@ -625,7 +876,11 @@ def compute_threshold(values: list, stdv_pct: float=1, min_thresh: int=50): \end{lstlisting} \end{figure} -An example of the thresholds yielded by this new computation is shown in Figure \ref{fig:threshold-refined}, where the green line representing the threshold successfully captures all six peak frequencies of the audio signal while simultaneously staying just out of reach from the frequencies of the background noise. +An example of the thresholds yielded by this new computation is shown +in Figure \ref{fig:threshold-refined}, where the green line +representing the threshold successfully captures all six peak +frequencies of the audio signal while simultaneously staying just out +of reach from the frequencies of the background noise. \begin{figure}[h] \centering @@ -637,12 +892,22 @@ An example of the thresholds yielded by this new computation is shown in Figure \newpage \subsection{Filtering through Similar Peak Frequencies} \label{subsec:filtering-similar-peak-frequencies} -While the new threshold calculation does properly capture all the peak frequencies, it also captures more samples than just the six tips of each peak. -These extra samples can cause confusion when trying to extract each centerpiece's state if they correspond to different states for the same centerpiece. -That said, as long as the power of each frequency is also saved, then any disagreements about a specific centerpiece's state can be resolved by accepting the one with the most intense frequency as the actual state. -In code, this is achieved by adding a temporary dictionary to the \code{get\_state\_by\_freqs} method defined in Section \ref{subsec:translating-freqs-to-state} to track the strongest frequency associated with each face. -Then, while iterating through the captured peak frequencies, only the strongest one for each face is used to determine the active centerpiece state at that time step. +While the new threshold calculation does properly capture all the peak +frequencies, it also captures more samples than just the six tips of +each peak. These extra samples can cause confusion when trying to +extract each centerpiece's state if they correspond to different states +for the same centerpiece. That said, as long as the power of each +frequency is also saved, then any disagreements about a specific +centerpiece's state can be resolved by accepting the one with the most +intense frequency as the actual state. + +In code, this is achieved by adding a temporary dictionary to the +\code{get\_state\_by\_freqs} method defined in Section +\ref{subsec:translating-freqs-to-state} to track the strongest +frequency associated with each face. Then, while iterating through the +captured peak frequencies, only the strongest one for each face is used +to determine the active centerpiece state at that time step. \begin{figure}[h] \caption{Updated version of state extraction in Figure \ref{fig:code-extract-important-freqs}} @@ -661,8 +926,10 @@ def get_state_from_freqs(important_freqs: list) -> dict: \end{lstlisting} \end{figure} -Using this new version of \code{get\_state\_by\_freqs} works just like it did in Figure \ref{fig:code-get-state-from-freqs-demo}. -Here the extracted state is the one encoded by the frequencies depicted in Figure \ref{fig:threshold-refined}. +Using this new version of \code{get\_state\_by\_freqs} works just like +it did in Figure \ref{fig:code-get-state-from-freqs-demo}. Here the +extracted state is the one encoded by the frequencies depicted in +Figure \ref{fig:threshold-refined}. \begin{figure}[h] \caption{Example: Refined conversion of peak frequencies to states} @@ -689,19 +956,35 @@ print(detected_state) \newpage \subsection{Ignoring Noise when Extracting Move Sequences} \label{subsec:ignoring-noise-when-extracting-move-sequences} -However, despite these noise filtering measures, the background noise occasionally causes the detection of an incorrect centerpiece state. -Since the approach in Section \ref{subsec:extract-moves} registers an applied face turn for \emph{any} detected change in a centerpiece's state, \emph{any} mis-detection would incorrectly register a face turn that never happened. -Fortunately, this issue can be mitigated by requiring that the state change persists over several time steps instead of blindly accepting any detected change in a centerpiece's state as a new face turn. - -This is implemented by adding a sliding window to the \code{detect\_moves} function first created in Section \ref{subsec:extract-moves}. -The window implementation starts with a new \code{window\_size} function parameter to control the number of consecutive time steps over which a state change has to persist before it is recorded as a move applied to the cube. -Within the function, two new dictionaries are created to serve as a "staging area" for state changes: one to stage the new state and the second to record the index of the time step where the state first changed. -As the function iterates through each time step, it updates these staging dictionaries each time a new state is detected. -Then, it checks to see if the current index of iteration is \code{window\_size} steps after the last reported state change. -If so, the current state is updated to the value of the staged state, the change is recorded as a new move, and the window counter gets reset. -Otherwise, the iteration continues until a new state is detected or the \code{window\_size} is reached. -The full code for this is shown in Figure \ref{fig:code-detect-moves-new}. +However, despite these noise filtering measures, the background noise +occasionally causes the detection of an incorrect centerpiece state. +Since the approach in Section \ref{subsec:extract-moves} registers an +applied face turn for \emph{any} detected change in a centerpiece's +state, \emph{any} mis-detection would incorrectly register a face turn +that never happened. + +Fortunately, this issue can be mitigated by requiring that the state +change persists over several time steps instead of blindly accepting +any detected change in a centerpiece's state as a new face turn. + +This is implemented by adding a sliding window to the +\code{detect\_moves} function first created in Section +\ref{subsec:extract-moves}. The window implementation starts with a new +\code{window\_size} function parameter to control the number of +consecutive time steps over which a state change has to persist before +it is recorded as a move applied to the cube. Within the function, two +new dictionaries are created to serve as a "staging area" for state +changes: one to stage the new state and the second to record the index +of the time step where the state first changed. As the function +iterates through each time step, it updates these staging dictionaries +each time a new state is detected. Then, it checks to see if the +current index of iteration is \code{window\_size} steps after the last +reported state change. If so, the current state is updated to the value +of the staged state, the change is recorded as a new move, and the +window counter gets reset. Otherwise, the iteration continues until a +new state is detected or the \code{window\_size} is reached. The full +code for this is shown in Figure \ref{fig:code-detect-moves-new}. \begin{figure}[h] @@ -737,7 +1020,10 @@ def detect_moves(state_over_time, window_size=8): # Edit \end{lstlisting} \end{figure} -And to validate that these changes work as expected, Figure \ref{fig:code-detect-moves-new-demo} runs the realistic audio through this new \code{detect\_moves} function and compares the returned move sequence with the one originally transmitted in the audio. +And to validate that these changes work as expected, Figure +\ref{fig:code-detect-moves-new-demo} runs the realistic audio through +this new \code{detect\_moves} function and compares the returned move +sequence with the one originally transmitted in the audio. \begin{figure}[h] \caption{Example: Refined move sequence extraction} @@ -761,16 +1047,39 @@ print(f"Matches demo_alg? {demo_alg == pretty_moves}") \end{subfigure} \end{figure} -And once again, the detected move sequence perfectly matches the transmitted signal despite both the reduced strength of the synthetic audio after being recorded through the air and the presence of the added noise of solving a speedcube, a result which further validates this approach as a viable proof-of-concept for tracking the moves of a Rubik's Cube using sound. +And once again, the detected move sequence perfectly matches the +transmitted signal despite both the reduced strength of the synthetic +audio after being recorded through the air and the presence of the +added noise of solving a speedcube, a result which further validates +this approach as a viable proof-of-concept for tracking the moves of a +Rubik's Cube using sound. \subsection{Optimizing Algorithm Parameters} \label{subsec:optimizing-params} -Significant testing went into determining the best default values for each of the three new function parameters \code{stdv\_pct} (Section \ref{subsec:fine-tuning-threshold}), \code{min\_thresh} (Section \ref{subsec:fine-tuning-threshold}), and \code{window\_size} (Section \ref{subsec:ignoring-noise-when-extracting-move-sequences}). -While each cube responded differently to various combinations of settings, this testing discovered multiple combinations for each cube that would yield a perfect extraction of the original move sequence. -A detailed exploration of this testing and its findings is discussed in Chapter \ref{Chapter7}. + +Significant testing went into determining the best default values for +each of the three new function parameters \code{stdv\_pct} (Section +\ref{subsec:fine-tuning-threshold}), \code{min\_thresh} (Section +\ref{subsec:fine-tuning-threshold}), and \code{window\_size} (Section +\ref{subsec:ignoring-noise-when-extracting-move-sequences}). While each +cube responded differently to various combinations of settings, this +testing discovered multiple combinations for each cube that would yield +a perfect extraction of the original move sequence. A detailed +exploration of this testing and its findings is discussed in Chapter +\ref{Chapter7}. \section{Summary} -In summary, this chapter demonstrated a proof of concept for a sound analysis algorithm that could detect the face turns of a speedcube equipped with the proper transmitter (the details of which are explored in Chapter \ref{Chapter6}). -This proof of concept algorithm works by computing the spectrogram of the transmitted audio, extracting the most intense frequencies from each time step, converting those dominant frequencies to centerpiece states at that time step, then iterating through that list of states over time to detect changes caused by face turns. -Several noise mitigation measures were employed to help mitigate the impact of loud cubes and other background tones. -Ultimately, this algorithm design successfully decoded both a purely synthetic audio signal and a realistic audio signal created by re-recording the synthetic audio while solving a speedcube. + +In summary, this chapter demonstrated a proof of concept for a sound +analysis algorithm that could detect the face turns of a speedcube +equipped with the proper transmitter (the details of which are explored +in Chapter \ref{Chapter6}). This proof of concept algorithm works by +computing the spectrogram of the transmitted audio, extracting the most +intense frequencies from each time step, converting those dominant +frequencies to centerpiece states at that time step, then iterating +through that list of states over time to detect changes caused by face +turns. Several noise mitigation measures were employed to help mitigate +the impact of loud cubes and other background tones. Ultimately, this +algorithm design successfully decoded both a purely synthetic audio +signal and a realistic audio signal created by re-recording the +synthetic audio while solving a speedcube. diff --git a/thesis/draft/Chapters/Chapter6-PCB-Design.tex b/thesis/draft/Chapters/Chapter6-PCB-Design.tex index 7746e33..acbde2e 100644 --- a/thesis/draft/Chapters/Chapter6-PCB-Design.tex +++ b/thesis/draft/Chapters/Chapter6-PCB-Design.tex @@ -5,24 +5,47 @@ \section{Introduction} -The transmitter for the sound-based move tracking protocol is in charge of creating the tones representing each centerpiece's current state, and updating those tones each time a centerpiece changes state. -This chapter will detail a proof-of-concept design for a printed circuit board (PCB) containing only nine discrete components capable of generating all the tones required for encoding one centerpiece's state. +The transmitter for the sound-based move tracking protocol is in charge +of creating the tones representing each centerpiece's current state, +and updating those tones each time a centerpiece changes state. -This chapter assumes that the reader has a knowledge of basic circuit components like resistors and capacitors. +This chapter will detail a proof-of-concept design for a printed +circuit board (PCB) containing only nine discrete components capable of +generating all the tones required for encoding one centerpiece's state. + +This chapter assumes that the reader has a knowledge of basic circuit +components like resistors and capacitors. TODO add section references (once the rest of the chapter is complete) \section{Requirements} \label{sec:transmitter-requirements} -This section will detail the constraints within which the transmitter will be required to operate. These constraints include the physical size of the transmitter (Section \ref{subsec:prospects-of-miniaturization}), the precision of tone generation (Section \ref{subsec:precision-of-tone-generation}), the reliability of the transmitter in changing tones to reflect a face turn (Section \ref{subsec:responsiveness-to-face-turns}), and the intensity of output audio that the transmitter can produce (Section \ref{subsec:transmitter-signal-to-noise-ratio}). + +This section will detail the constraints within which the transmitter +will be required to operate. These constraints include the physical +size of the transmitter (Section +\ref{subsec:prospects-of-miniaturization}), the precision of tone +generation (Section \ref{subsec:precision-of-tone-generation}), the +reliability of the transmitter in changing tones to reflect a face turn +(Section \ref{subsec:responsiveness-to-face-turns}), and the intensity +of output audio that the transmitter can produce (Section +\ref{subsec:transmitter-signal-to-noise-ratio}). \subsection{Prospects of Miniaturization} \label{subsec:prospects-of-miniaturization} -The transmitter must be both removable and small enough to fit in the center cap of each face of a speedcube. -This requirement stems from two sources. -First, in contrast to all existing smartcubes, most non-smartcubes have small, solid cores (Figure \ref{fig:356-core-closed}) that provide no extra space for the inclusion of any electronics, but do have a small amount of open space within their center cubies (Figure \ref{fig:356-core-open}). -Second, the use of a cube with non-removable, embedded electronics violates the WCA competition regulation 2i \cite{wca-regulations} (See also Section \ref{subsec:competition-regulations}). + +The transmitter must be both removable and small enough to fit in the +center cap of each face of a speedcube. This requirement stems from two +sources. First, in contrast to all existing smartcubes, most +non-smartcubes have small, solid cores (Figure +\ref{fig:356-core-closed}) that provide no extra space for the +inclusion of any electronics, but do have a small amount of open space +within their center cubies (Figure \ref{fig:356-core-open}). Second, +the use of a cube with non-removable, embedded electronics violates the +WCA competition regulation 2i \cite{wca-regulations} (See also Section +\ref{subsec:competition-regulations}). + \begin{figure}[h] \centering \caption{Internal pieces of a standard speedcube (Gans 356)} @@ -43,37 +66,73 @@ Second, the use of a cube with non-removable, embedded electronics violates the \subsection{Precision of Tone Generation} \label{subsec:precision-of-tone-generation} -While the receiver specified in Chapter \ref{Chapter5} supports custom state to frequency mappings, it expects that the frequency corresponding to each centerpiece's state stays constant throughout the entire audio recording. -As such, the chosen transmitter design can encode centerpiece states with any frequency (assuming the chosen frequencies work within the constraints specified in Section \ref{sec:protocol-requirements}), but it must produce its chosen frequencies with high precision. + +While the receiver specified in Chapter \ref{Chapter5} supports custom +state to frequency mappings, it expects that the frequency +corresponding to each centerpiece's state stays constant throughout the +entire audio recording. As such, the chosen transmitter design can +encode centerpiece states with any frequency (assuming the chosen +frequencies work within the constraints specified in Section +\ref{sec:protocol-requirements}), but it must produce its chosen +frequencies with high precision. \subsection{Responsiveness to Face Turns} \label{subsec:responsiveness-to-face-turns} -The chosen transmitter design must respond to an applied face turn by changing the currently transmitted audio frequency to the frequency corresponding to the new centerpiece state. -Since top speedcubers can reach a burst turn speed of 20 TPS (see Section \ref{sec:alternatives}), this means this frequency change must complete within 50ms. + +The chosen transmitter design must respond to an applied face turn by +changing the currently transmitted audio frequency to the frequency +corresponding to the new centerpiece state. Since top speedcubers can +reach a burst turn speed of 20 TPS (see Section +\ref{sec:alternatives}), this means this frequency change must complete +within 50ms. \subsection{Signal-to-Noise Ratio} \label{subsec:transmitter-signal-to-noise-ratio} -The transmitter must create tones loud enough to be easily distinguished from ambient noise, including the sound of the Rubik's Cube's own turns. -In light of the above requirement for the transmitter to fit within a center cubie (\ref{subsec:prospects-of-miniaturization}), this requirement will also require the transmitter design to consider how to overcome any audio dampening caused by such an enclosure. + +The transmitter must create tones loud enough to be easily +distinguished from ambient noise, including the sound of the Rubik's +Cube's own turns. In light of the above requirement for the transmitter +to fit within a center cubie +(\ref{subsec:prospects-of-miniaturization}), this requirement will also +require the transmitter design to consider how to overcome any audio +dampening caused by such an enclosure. \newpage \section{Design} \label{sec:transmitter-design} -Given the above constrains, this section will detail a design for a printed circuit board capable of precisely generating four distinct tones. + +Given the above constrains, this section will detail a design for a +printed circuit board capable of precisely generating four distinct +tones. \subsection{The 555 Timer} \label{sec:the-555-timer} -The core component in this PCB design is a 555 timer, which is a computer chip that facilitates the generation of many types of voltage frequencies in a circuit. - -For this transmitter, the 555 timer will be configured to output a square wave voltage signal with a 50\% duty cycle (i.e. "Astable Operation") \cite{icm7555}. -This means the voltage on the output pin will alternate between low and high, spending equal amounts of time in each state. -The attentive reader will notice that the square wave signal proposed here differs from the sine wave used to create the synthetic audio in Figure \ref{fig:code-generate-alg-audio}. -Valid concerns may even be raised about the fact that a square wave is actually a composite of a fundamental sine wave and infinitely many harmonics \cite{harmonics}. -However, since the nearest harmonic in a square wave with a 50\% duty cycle oscillates at a frequency three times as fast as the fundamental \cite{square-waves}, then choosing a fundamental frequency of at least 6.67kHz places the nearest harmonics beyond the range of frequencies measurable by a typical smartphone or laptop microphone (see Section \ref{subsec:frequency-response-range}). - -The standard schematic for creating this type of voltage signal is depicted in Figure \ref{fig:555_astable}. +The core component in this PCB design is a 555 timer, which is a +computer chip that facilitates the generation of many types of voltage +frequencies in a circuit. + +For this transmitter, the 555 timer will be configured to output a +square wave voltage signal with a 50\% duty cycle (i.e. "Astable +Operation") \cite{icm7555}. This means the voltage on the output pin +will alternate between low and high, spending equal amounts of time in +each state. + +The attentive reader will notice that the square wave signal proposed +here differs from the sine wave used to create the synthetic audio in +Figure \ref{fig:code-generate-alg-audio}. Valid concerns may even be +raised about the fact that a square wave is actually a composite of a +fundamental sine wave and infinitely many harmonics \cite{harmonics}. +However, since the nearest harmonic in a square wave with a 50\% duty +cycle oscillates at a frequency three times as fast as the fundamental +\cite{square-waves}, then choosing a fundamental frequency of at least +6.67kHz places the nearest harmonics beyond the range of frequencies +measurable by a typical smartphone or laptop microphone (see Section +\ref{subsec:frequency-response-range}). + +The standard schematic for creating this type of voltage signal is +depicted in Figure \ref{fig:555_astable}. \begin{figure}[h] \centering @@ -82,16 +141,29 @@ The standard schematic for creating this type of voltage signal is depicted in F \includegraphics[width=0.75\linewidth]{Figures/6 PCB Design/555_astable.png} \end{figure} -The two key components are the resistor \code{R} and the capacitor \code{C} whose respective resistance and capacitance control the output frequency \code{f} via the relation shown in Equation \ref{eq:555-freq} \cite{icm7555}: +The two key components are the resistor \code{R} and the capacitor +\code{C} whose respective resistance and capacitance control the output +frequency \code{f} via the relation shown in Equation \ref{eq:555-freq} +\cite{icm7555}: + \begin{equation}\label{eq:555-freq} f = \frac{1}{1.4 R C} \end{equation} \subsection{Creating Audio} -The 555 timer's varying voltage output can be easily converted to audible sound by attaching a voltage controlled speaker to the output wire coming from pin 3 in Figure \ref{fig:555_astable}. -However, this will only produce one continuous tone, and each centerpiece will need to be able to switch between four distinct tones. -As such, the resistor \code{R} will need to be replaced with four separate resistors (labeled \code{R1}, \code{R2}, \code{R3}, \code{R4} in Figure \ref{fig:555_astable_modded}) and a switch. -The switch will be connected to the cube so that a 90$^\circ$ rotation will change which resistor is in series with the circuit, thus changing the output audio frequency of the speaker. + +The 555 timer's varying voltage output can be easily converted to +audible sound by attaching a voltage controlled speaker to the output +wire coming from pin 3 in Figure \ref{fig:555_astable}. However, this +will only produce one continuous tone, and each centerpiece will need +to be able to switch between four distinct tones. As such, the resistor +\code{R} will need to be replaced with four separate resistors (labeled +\code{R1}, \code{R2}, \code{R3}, \code{R4} in Figure +\ref{fig:555_astable_modded}) and a switch. The switch will be +connected to the cube so that a 90$^\circ$ rotation will change which +resistor is in series with the circuit, thus changing the output audio +frequency of the speaker. + \begin{figure}[h] \centering \caption{Centerpiece State Transmitter Circuit} @@ -99,21 +171,42 @@ The switch will be connected to the cube so that a 90$^\circ$ rotation will chan \includegraphics[width=\linewidth]{Figures/6 PCB Design/555_astable_modded.png} \end{figure} -Alternatively, it is mathematically valid to instead switch between four different capacitors of different capacitance. -However, for the reasons described in Section \ref{subsubsec:freq-selection}, opting to switch between resistors proved more practical. +Alternatively, it is mathematically valid to instead switch between +four different capacitors of different capacitance. However, for the +reasons described in Section \ref{subsubsec:freq-selection}, opting to +switch between resistors proved more practical. \subsubsection{Choosing the values of \code{R} and \code{C}} \label{subsubsec:freq-selection} -There are infinitely many combinations of \code{R} and \code{C} that will produce any single desired output frequency \code{f}; however, the set of resistances and capacitances of commonly produced resistors and capacitors is finite. -While multiple components can be combined to produce more specific resistances and capacitances, doing so would increase the overall component count for a circuit board that is constrained to a small physical size. -As such, this proof-of-concept design will focus on producing frequencies attainable with a single capacitor and resistor pair. -Since each centerpiece transmitter needs to produce four distinct frequencies, four separate capacitor-resistor pairs are required. -However, since variation in the capacitance \emph{or} resistance produce variation in the output frequency, one of those two options can be held constant to reduce the overall component count. - -To chose which one to hold constant, the output frequencies of all possible pairings of capacitors and resistors from two cheap Amazon kits \cite{amazon-resistors} \cite{amazon-capacitors} were calculated using Equation \ref{eq:555-freq}. -The resulting table was then color coded to highlight the usable frequencies in green (6.67kHz to 20kHz)\footnote{6.67kHz is the lower bound derived in Section \ref{sec:the-555-timer} and 20kHz the upper limit of the typical frequency response range discussed in Section \ref{subsec:frequency-response-range}} while leaving all other unusable frequencies in red. -The result is shown in Figure \ref{fig:freq-selection} with the various resistances shown on the vertical axis and the various capacitances along the horizontal axis. +There are infinitely many combinations of \code{R} and \code{C} that +will produce any single desired output frequency \code{f}; however, the +set of resistances and capacitances of commonly produced resistors and +capacitors is finite. While multiple components can be combined to +produce more specific resistances and capacitances, doing so would +increase the overall component count for a circuit board that is +constrained to a small physical size. As such, this proof-of-concept +design will focus on producing frequencies attainable with a single +capacitor and resistor pair. + +Since each centerpiece transmitter needs to produce four distinct +frequencies, four separate capacitor-resistor pairs are required. +However, since variation in the capacitance \emph{or} resistance +produce variation in the output frequency, one of those two options can +be held constant to reduce the overall component count. + +To chose which one to hold constant, the output frequencies of all +possible pairings of capacitors and resistors from two cheap Amazon +kits \cite{amazon-resistors} \cite{amazon-capacitors} were calculated +using Equation \ref{eq:555-freq}. The resulting table was then color +coded to highlight the usable frequencies in green (6.67kHz to +20kHz)\footnote{6.67kHz is the lower bound derived in Section +\ref{sec:the-555-timer} and 20kHz the upper limit of the typical +frequency response range discussed in Section +\ref{subsec:frequency-response-range}} while leaving all other unusable +frequencies in red. The result is shown in Figure +\ref{fig:freq-selection} with the various resistances shown on the +vertical axis and the various capacitances along the horizontal axis. \begin{figure}[h] \centering @@ -122,10 +215,19 @@ The result is shown in Figure \ref{fig:freq-selection} with the various resistan \includegraphics[width=\linewidth]{Figures/6 PCB Design/freq_selection.png} \end{figure} -A close observation of the resulting sigmoid shape of usable frequencies reveals only one location where there are at least four usable frequencies associated with a fixed resistance (i.e. row of green cells), compared to ten locations where there are at least four usable frequencies associated with a fixed capacitance (i.e. column of green cells). -As such, the most practical value to hold constant here is the capacitance. +A close observation of the resulting sigmoid shape of usable +frequencies reveals only one location where there are at least four +usable frequencies associated with a fixed resistance (i.e. row of +green cells), compared to ten locations where there are at least four +usable frequencies associated with a fixed capacitance (i.e. column of +green cells). As such, the most practical value to hold constant here +is the capacitance. -One of the locations with a viable fixed capacitance is shown in Figure \ref{fig:freq-selection-r}. This pairing of a 10nF capacitor with four resistors with respective resistances of 4.7k$\Omega$, 5.1k$\Omega$, 5.6k$\Omega$, and 7.5k$\Omega$ will be used in the prototype created in Section \ref{sec:prototype}. +One of the locations with a viable fixed capacitance is shown in Figure +\ref{fig:freq-selection-r}. This pairing of a 10nF capacitor with four +resistors with respective resistances of 4.7k$\Omega$, 5.1k$\Omega$, +5.6k$\Omega$, and 7.5k$\Omega$ will be used in the prototype created in +Section \ref{sec:prototype}. \begin{figure}[h] \centering @@ -138,7 +240,14 @@ One of the locations with a viable fixed capacitance is shown in Figure \ref{fig \newpage \section{Prototyping} \label{sec:prototype} -With the schematic drawn in Figure \ref{fig:555_astable_modded} and the specific values of \code{C}, \code{R1}, \code{R2}, \code{R3}, and \code{R4} determined in Figure \ref{fig:freq-selection-r}, the next step is to wire everything up on a breadboard to create a physical proof-of-concept. Figure \ref{fig:breadboard} shows one such breadboard with all the components labelled, with the exception of the rotary switch which is represented by the vertical green wire on the right side of the board. + +With the schematic drawn in Figure \ref{fig:555_astable_modded} and the +specific values of \code{C}, \code{R1}, \code{R2}, \code{R3}, and +\code{R4} determined in Figure \ref{fig:freq-selection-r}, the next +step is to wire everything up on a breadboard to create a physical +proof-of-concept. Figure \ref{fig:breadboard} shows one such breadboard +with all the components labelled, except for the rotary switch which is +represented by the vertical green wire on the right side of the board. \begin{figure}[h] \centering @@ -155,7 +264,9 @@ TODO detail the process of building a prototype. \section{Miniaturization} -- highlight the value of so few components for the physical size requirement, e.g. the prospects of A SMD version of the board + +- highlight the value of so few components for the physical size +requirement, e.g. the prospects of A SMD version of the board - Give a final part count. @@ -166,6 +277,7 @@ TODO detail the process of building a prototype. - If possible, create a demo in KiCAD... \section{Minimizing Sound Obstruction} + TODO Discuss the "tupperware" tests -> design of various center caps. \section{Summary} diff --git a/thesis/draft/main.pdf b/thesis/draft/main.pdf index bfeda55..cd7273f 100644 --- a/thesis/draft/main.pdf +++ b/thesis/draft/main.pdf @@ -607,7 +607,7 @@ xڭU �zv1�IE��{p���`��T� �� �^"���0¨��JrP�<-��{J����F9K޼�c@0�댌���4#���\U$8�@�$�*pF2����+�����i��f!���Gh�6D��+i�R<�@W����.�u9ˋo�h?�D��MJb,6'm���W�� �6j����d�4p�v��?��]}��ݼ� �/�6��u����b-A ���i�7��X0ڻ�Ӣ<��v@m����4��߼FN�8�(�C��\J`�!1)�:يY�Z9KW�UX�֏�yY���9_�y���Pʳl�.˰�OZ�(���җ�<,*�U� {[^֢�7�$/v���48����|�5�d�iCm�Oʷ��v��|���|9��6�Eycl8Y Rhܫ��s��6� (p[w��:+���,ϳ�vr;T���" -+Hm���CT)�/�|�5=ɳ,��[�Y��@�ì\����7�qQ�r���F�;�/���` ++Hm���CT)�/�|�5=ɳ,��[�Y��@�ì\����7�qY�r���F�;�/���a endstream endobj 398 0 obj @@ -2899,22 +2899,22 @@ endobj endobj 736 0 obj << -/Length 1936 +/Length 1948 /Filter /FlateDecode >> stream -xڥˎ�6�>_���h7-�z�-�� ��=�p|`K�n��Z=�=��T�Hu�� ���b�X�w) -�A��Dn}�x��m��4�#�r7vjt/yLߍ��' �;�m�AP�����"K�Q� �K�<�8�u��RӱU�}�ϵ JoG��Ĉ�j�ݱ͏ �ʋ���\g���X,S�� -���χk�oLd�>�u�˧{�~���YL*��#O��+]� -���F�(��VG��𦥵�JGh����n�D��:j��[��c��4I�Tf�p��]74� &��9_`�LDL�������9юR��88#�I��k��%3�� ��Z���r� )�e-� -d�A���J�j:����\��B��]���zY|����4����'GH��ۊ���N�j��j��R��*��ե�N��٦ۚ�]��є�������g=u�t΢ SȈ� ��n���y���� -�Q��ܩ����4�nl:�f:D��G߈���Z�T��6Vn���շC]�����5O]�&S� �������g2��J�a/��~�x>;@����8�qQqt���u���W^�VY�jNRn�{=���4/�K_��pi�a;N�� �X�I���%�zbψׇ9I,�o�K�Ǯ������TZ�g�r ��{w@�q2uM؝*��r��BZ8����GR�V���z�-�7� Q��� ���@��R ���4�9���L���x��b���@׌��g��#hc�nt�s�kJ -{ ��M���[��<�1�3l ���� �m�n�6�;���eH�T�E,���E&i ��k�w��'ePQ"��$cQ���_�H�*o��' 3��=��v�W쓀'� �I����7�6d��Y�X+�Y���NTYm�m��J�pr������a���՟�R~o�_y�&�A��#��N�FAe�jy��8a�5 -ǯ�3��{�L�r#a)�]G{���,)�B|Q�`C�9�׸�x���I�ҳ�|��:��Rw�o3��z”�<�ɝ;/@����xt�~"MX�����r���CW�\~�s�!��/�4� d,M��5��m�69�n�,:"� f��Ot ��ך�ݫ���˜�*g<+�:G��`�,�C->�X��n-�Pc�}�L��[���`�I��=�eB*㘶�bS`[~�Rx[�{w��۷u�`\�uz�+O� &��~O�79 qy�v���iS����>�d\'��,ʗZ,�i�~8YJ�0^r� �N�$�i�;]�݂�Fʩg9|�:���7,��Ǜ�����xn4���E -��L;9+� ƅ�k�~� ZF��'z�y]W���qx�x�`� O�i��es��STp� �"N�� x����h���7�^�+����Ep��xq�W���PFӘ -B���P�]��6�)L�_T�� V ė����D�����^��n��I�_����~�-�7uG/a�2Y���Ћĝ��3D��7Z��O���I�X|h9v-=��gU�u[9�!��6fn��1��� )i���dy��ur�Z��B��_���#��7)S;[��lG!���f�_���m"�`YK�%x⥍�7<������Kr�> ˸~��� %���������� -��J�4 +xڥˎܸ�>_����nZ$���;Y��b{��������=�=��T�Hu�۳��'���b�ޥ(�Qp���Ǜ7�<�+���� .r��"�d̲���S�o??�����7�R�� ���zsɀ�&�,�[-뻃�'=�nD�=������J�$eI���������t�B�c&�T8�4f������(��ǃ�[�VG�Nw;Z?4jp��y�G�V p �<%�z������ݿ��H.� ��p��w�]Q�,�( 9��8=�F?�ZÍ��6Oڑx^��y�i$, +Gh�N���n�7(? + O�w4�Z��"��iE����-A +�,ny�q��^���m�(X���k5��20�$#���P�m�ȕ᧳��J��X� ������tޘ��S}&�G�8�W�� |���YL*��#O��+]� +���jQ\J�� +��𦥵�JGh����n�L�u�t�� +C���i������^�nh�.L��s������1���%�-(�s��t�88#�I��k��%3�' ���aK�q`<(2� ���.(����*��i �\{�!���.�da�,��i�}���ԓ#$}�mE[BT'Z5QM5�~�ZB�]���a�num�mM�V�hJwo�����YO]7���(�2b����A����߫� ]?-'�+))F)ͤ��,��Q����7�����1ՠ���[�bjA��PW;�=��#@�Sרɔ��<�t��|r�L��Y�3��V�ϣ��c(�^�njcG�DL�;p�-��>�50���$��'�o�'�Y����e�r�+.�?l�I�6�"�6�w��d\O���x '�� wI���3�P�3�J+|F,�P��w$'Sׄݩ��11(��.��=+-�O�z$�iU9Y�`��Ry�0����Ɋ +T*.��8J�c�:ʤ�*��(�|�7t�x)�y�<A�t�۝{\Sz(|kH�nk2�?�����a�Q�a��N��v�L`s��q��\��NUL�V�"�HXd�����O}R%�ZH2�Y Y��Y����6i�x"�01�_3�?/��}�d���>i��� � Y�8kV,��|@h��UV�a[:��9�u~�D�k�dX��z�g���[�W��I�b�?����׵QP�Z^�?NXz&F�B��� ��^�F����HX +c�·i�e��B!�(\�A�9�׸�x���I�ҳ�|��:��Rw�o3��z”�<���;/@����xt�~"MX�����r���CW�R~�s�!��/�4� d,M��5��m�69��VYtD�A�<:�9��,H5�5�;W̓ =�1�U�xV�u����Y2�Z|����^�Z�A��rl3�oIR�&!;��� ��c�vD�M�m�mcH�mY@����o�֝�qE����<%.��n���|����-g+�.�s��d�:oQ(x���4��L�T�N��Y�/�X�������a���� P}d�����t7R�=���Y��5�b�7>�����F�o��Q$���~ʴ���`\����W=�et�w} �i���Y�[�_o�{��� ��b��\�ln>}�� +�~ŒY���R6O3��u���_�����`�"c���/.�7X� +lS�(���k/��v6�y�jz;��"�ȳ^��Q�h�s����U� Ri�[�<�ُ�t��"�aF"�Zoz������s���A�^�y����4 +Ξ�pG#x9�%��.�{c�l��#�^:Xp����<|Gv T�s؝�Ƴ��H�LB/�α{h�E(?�"���ɤ�\�1sC�����~lHIC�Ո�$��l��{���"�΋�@��I�ڑ�@�f;$dG9K�*��m� �Zڈ�-�/m�����}ww�Y���aY��'^3��N��N��K+����Q;� endstream endobj 735 0 obj @@ -25214,16 +25214,13 @@ endobj endobj 1017 0 obj << -/Length 1678 +/Length 1680 /Filter /FlateDecode >> stream -xڭXKs�6 ��W�Vy�ˈz+��m:�L;m�[�W��r,���q}��J%uS�� ��GH�w���?�߼z��Xܻ?xq�(��,�Y��}�}�������y�s�WoR��<�Q�.rE���#ùF��It��owa� d��p~��o��,����$�w��� [��(NC�'�Y�I��� �_۱��T�J��q�&,��Ⱘ����;�p�����V��dE�F���؋�A�G�uF�u�k���-��I��ݯ4V��լG�h2��W�x��&ٖ6F�ǃp�q���2Ɍ�.�����ШqD7�!�Ji�� -�� �-�FR�vz�����u���L�H�q�G�L���k�h���0�nx�JI�F�5.&�8[�|�%��m����EG�>"�<�7)��\�:U�k� ?]���N���� �j����m�s�[���(�nx�������mF1ʵ+��Q�nd;~݅xӅ�0�?�f��������A7N ��Uj��?a��y��j�+��GU�Į�P���>l�gY��@+�lX4�a|s��B}ԽO MP����[�����&"�Ca��b�# We�ej�4��@�P �jFhF5�-Q�@克,�=�^���9 -���DE�s�${��n�d���ԂԨJ��ʥ=�E[�`�@"�'��V���ЭIy�bC�f驮����p@����FAûi�L��ܿ�t6d��:��:��ZU%EM��zb�$�{9?g7 є�Q�%i��PK[�w��И��We��9�A���i�m�Z����j���)L�`M��2��]�N���d��K�\{�w����Q��u�Q�̰�*�A���PAƇEa�[��GSz@��-Q�0�Ʊ��>�ўH��$B������@��&}�Z V�Ah:���n.5�^j&�۱Dm���L5A;'���l͆N� -@����=+r��u��IXP��� ˳e¢� +ۓ�GʹR�M�JOC����\[�Ձ��f�NMc�D&��������‰�� ׏���֨�uP��<�U�E�� -*^k�#M:K� �[t��;cE��;LSV���v�|u�u��s�fs��f��������p�L�?� ��� �1�8���(Ӛ�M�P�}@���t�[�$�-� ����*�Hei�@=��E���?�idK������M_J�����b���罈g��9cV"���J٧ (Hl��돀X��O��n�e���{�mg�*! -҈nA]�di[*עB��`!�ߪ ��/���og]�M[������\����dn�.]�خ��=6��۶o��h�"n��� $L˩nT�]3��w,�G�����s�@ \^9�u.��������}{l���K�%B�' T�W��2���tÁ <�� (+����^��|�x,�E�E�=���i��"�����9��xˢq��/b/�YPd p4b9fj~�t� �!��,����y��J0���vZ�k���� �O�l���I_�'�Tt���#>�5��Q��:iX�i^9ե�{J�=Ͳ �T;ZM���S�4�o������^��z�{�ܯ�ܟ�vF}A���4D��ٷ���l�8���D���1L��>0���qR� �ë��qd����K#d�?��!� +xڭXKs�6 ��W�Vy�ˈz+��m:�L;m�[��V��r,���q}��J%uS�� ��GH�w���?�߼z��Xܻ?xq�(��,�Y��}�}�������y�s�WoR��<�Q�.rE���#ùF�ݩ�F����$�<�� ��<36�.IY�� �'A��D%���=��d�6��s��ZI�>#�<�7Q�|u��7b�/)쥔6,��\d+.����m�s��_7<��Q_yUM{�c9��+��Q�n���B���O���E���뿤�v�� 'J��*�\⟰b�<�J������lb�b�zi��6�ht�Q�s6,�4�a|,�����{9�Z�"@��O�I+����*�&$�Ca���`G��2��pi$�Z�2֌� k8[��� 'X\{���-m��cs,�+B,�Q=����- x�%>�'R��hjJ��Xm̓���LXEn\�B+��ņ��SS ��ဘ1$���% 尿|0�s��� ؐ�����8�R4�Z� ����W������,DSDIC����C-m�ߙ�Bc^�j�t�§ �)ja�˶k^���0�k���ΗI ��J���l�x�k/�.�\�:j��B6*���B�3U�*���(l{bKa�hJ�XՖ�8V�\���2�� s�D����p29ؤ�|��`5�����H�6��R3�fR�K��j����s���V�l脨D���/�Y�3��=O_$aAM��/$,ϖ �nl&�P�5�K�vq@XH8K= ��DsyT����͚���J#�Lb� (����Å��)��7���k��vy��=�.+�T����G�t�l���4 xƊ,0�w����S/b�m����F�6@C��n?���]���k�/��?���7@���A�c�q�-Q�72�$6�r��"u���BI�[ �-����!T����H�z������~��Ȗ�g�������8\��K���v�O�{���sƬD�K����OP��� ���7�h?<���N��{�mg�� +ˆnA]'Ee[*עB��`!�ߪ ��OD����nΦ��~mnh�[��|��q27R��Vl�ԋ���m�7@�T�Z7��h��ԴRa�Ls�! �Q(�[�pN��k 缎�Eþ��こ98��o�-��|��D����*� +�Y���7�n8��ǽ<e�Wp��«ڛ������ȽG#�z<�XZ$@7���?�3oY�#.�E��1 �l�F,�L���<<_��/�04OT �R�N+t�~�����<�m{a����$�:.<��nM;uԪ�N�l��NuU�=��fٖxR�VSI�۩{�?�p�x �ub/�w=����_C�OJͨ_�00 "�A�JX�3�M��ߙCٮ~d S3�Lmh�d;����$z�7�2����*"L endstream endobj 1016 0 obj @@ -25330,7 +25327,7 @@ endobj endobj 1020 0 obj << -/D [1016 0 R /XYZ 85.039 450.427 null] +/D [1016 0 R /XYZ 85.039 447.586 null] >> endobj 1021 0 obj @@ -25563,23 +25560,20 @@ endobj endobj 1065 0 obj << -/Length 3461 +/Length 3463 /Filter /FlateDecode >> stream -x��ZY���~ׯ$�A� �ɾ ��VV�lG��5�@�9���ΐ��ʋ �=_U59��юdy���G_UOu6�e�珲�y��oς�, YP��ˑ7i���ˋԹ0����$&�����,y�+�������g�d�뻺�.�j&�/������o����E��<7X��Ԇ�������/���H����6=[>z�6����(Ks4���Q�B���������M��j�T��{�!�!�;���,˒W�j]n�zwr[ -������ؘd# O��r���L�T�B���Z`�\{�K(+�ɮ�夾��j�Z�K��o�46��=���v��6�YȻ��MZ)�&�|֮'�i+o��m������3�FG�M2��R���sl?vPn��nKL�V����c�� ���B�h?�K*�e9/��ͺPʴ�=i��XڸxU�B³��OZ�*�IE�d��EU��J�MGZ�#�� �I!&�v��_W��F=�n����@C.'�Rz����30�w�n�u�O�7q��բb�@������]�sVVU9�c68Z,��(L���i�m�v{�P,��&.�D!r�d���jC�,�-�hhieϏjڬ -��GVipN�3uX�%�Nm�������p�}%D�)�*|j�Ü.uET3�ۦ��ߘ�XU-!��Ъ�����rr �|�A�H��ZE]e=��� �m�o"�jF�5�~q��U�0���{�I �m_6�%�/d���#�� �j*'$|���Fm�8(��p�`k 12C zD̥֨E2�2�d�@FR��#���������?��|��w��H�|�݋��_��(��_ǩ} - ��Jj)>���WezM��MN���0Z�z���?��YL�&��ۋd2mnl�=m��e��"��a�if�E%$;���j��2��a�`��K�`��`���0F2ۮ#,��õF��M- p@�lA\��$���/0<�3�,�����g`�X���~�����R�#݇%��t݇��V�\���ͻwK������S!}�sN�FN?/�2]���w�H�j�����1TEGwݷ�E5�t���k�[^�ʋ��D�U`#�#c|�=��ͱ��A�<7 2��,�z��)*�RV����ϔk`��w&=�}�ܤ.?:��S�d��#E��|_�O��v5����l�;�ʣ3�m�&�)ׇg�(�hB���6>���/�f߭���b=�,����/ o�jK��,`u�v֣��]\&ڋӹns/�gV'�9���W������B��K� O���jN?3�E<.;��!�nZvv��W��H������9=�a 9'·�"�NûQ�_P}7qJw��G��,$�� -.�Fj�����A��� ��l9�rZqz��ӧ����a� -\���3�M����^c��|(0�U�7c��[*q�k��H�TzM�s,��sp�p=)�>)l��*����3g��C� �����+L0�M��W6��G�AWR[�%��OKsD�r�� �Y����tr��>ǡ��@�KJ.(9��%<�~W���GJ^P�z�5;���?�8&'yw&q��t��$�qG���\sZq -� �Z�e�Gak$ʖ�VIVJv{P�`%K֖f�y�rV$��t�)�����Pڏ+X�P�J������!��@�`<�-�L~�ZiĜ!F�\v� �l�S� KPm�1� `��d����!�vE�mlD��^,�5.=���[� �����Jm7R�ژ��+��,H�_H��]��ݻ{��y��?<��ra��Db"�\�0�vd�i��ؐ��,�,(|�'�n�(i����{G��h|B���'����8��q��SY�p�����>��>d�/�ͩ��f��gHdB�0E�a�ʆ�ɦ�x�0�T�0�ȓ҂S�N�����K.7\��23�E�"�ڈ��"w �A -��V�e0��9�� l�gC\��=��K���#��4�׏{ o��N�;�a�^\�w������4>�U�V2F�g�f}��=�= Z1�\c)� ,���0�95 d���>{"/dG�Lj���F22(���2�.���(#�ƞ�od���k�ՁY���g����L��� +Hl Oot,�2�u����{(��Eq�Z\4��2�c�>��zH�p�7"����!�R����Q��+�咈��������p{"G��0�C�H�}>f��A=�X���P��?���dP� -�\I���V����A}d��>:c.�c�!��Z)���Z�URk�FbZ(q���9ȩ����U���W��/'�8k�9���PZPI�/���!@cp=� 2�!���1�����c�����9�z�)���n��?�qnP8� �#�p�r;�0��L�2u����;j%^ 6����쎆��@��S���>:�O@�0@�)L�G� �Ê|;�����9_'���\����<o��� -v��u��.eJ����� l}� (6Qj��#���jc�ٶ ��FfuJ {�{��� q��y��#��Zjd' ��� *Ɇ��tx��BN_�T1NA�##���N���奄���-��;��ms̵\J�Ɔ�J��^����6g� �� ��^g�щ�:٬�]���C��-(�����k�� ���y��k��[2��� _W�]�Gn௄+٧�����B��dЧ��?����i�`I�U����w���k.J�����o=�ڵ�ڨ��j�U{�|@`�e�^h)�V��wF��;�(�T��iʁ�DU�E�P� -��n�6E����5GC Scļ��[ - �gN�w����F:�7��w9/R=Q�����(�I�����RC�-v�9����ɢ���wn��u�8_W7Rzy�˶ڴ�*�7�K��(�]mx謹B8&��]�7-�g`|�P�MI �/����| g��Z|fAØ��8�L�3�8O��rl���fۮ�m,_J.�E�hUNn�15/��|.��c����_���Л�����_�ٶ{2@���U�ɤ�qD�A�� B���r���'�D��/Y]]r���?��,*���0��-k���?��c�h�����Yc��������$>ڐ^�����ә��,J ��41�� ]���~�0b��(�����!��'\A�m�+��S �~d�5��^��z_��_�e=�{�\�������+��-ZJ�w�c����̦�SäE�vw��AЭ'A� �Yn{�r�Ϫz���Y0�?���v*��f7me�H����^r�9=�^���X�G�~u -��zf���1�j�}��~���J)̷�Xz�ё����(���;*�Cy#�NE��33L'O���ٝtϮ'�U�}�{SÅ����M޴G���;�s����9R��a4 >��c��Ӳw�6��%�G\�R��=XF(�i(��� cl���x��_Xb�� +x��ZY���~ׯ$�A�L�f�?��Z��ȶ��Ȃ<w���x���"��WU͹��v$�K_l6������jp5P�g�T���x���bh�G���r\�l����2.��7��M>(t�MQ�=�z�ýey�^�Σsfo�>�<ڽ}��VJe/��ZW�n�ض4^n����s�Z�n�U����ܘB���a k�%���h׉�|���p=_Ϊ9h��Z���=���v4�1�,Md���F��&�F^kW�ɍ�U����'z��]��1�j�e�Q3���LA~z@��ꦭ�d[ߦ7�ߥ�"�g�ų��=D%����Vi��Ū�Pɲ�=m����q�j��g}��Z^J����(M좮п�jݱV�g��n�Ia&=�f���'��ʹ���M�M*y�nVMZ�YOO���m�j����.ѿ\�jf �K��40�50cer��f&�j����䱬+"���hj��1N�Ƨ�[lZ�F0EF��n��5�i��آaQdeT=;�X��:W� ��`�0gz�M��&�����%����F ��30�\�k�yY$3c��;���ŎMdx[th������cR f[� ��D�����`#��2��������R�l�����nXt�gݧ�z4O���.���ˏH�x���?�ޭp�h +�DY=pC~$�hU�E6�v���) +� ���1�櫗�?�>�����Z�'���ųW'��̺�Ǡ�}����u#�W��&A�|��+ +�u�\��q��o���|�h��h�=:�^</n ۬)�8彧&�7�(���u�u���@�qv��R�Qc�[|><�&{��k._ry�}q�,�޾�p�QYΥ��s�/B�'B�������-�� b��ձ��������`��.h���i�Z�$v!�0��EL�m�fRQtQ(:�Po����m��}�{\q:�p9����5�O�͡�ư8.)rŤR�'���������^g�!����o��#��S����1D�_@rE�e��g�W��n�<<+}�L�%�*���*\�����,h��s��K���} "���%�6����= F����*�����Q~�7�0 |N� ٯR�R1z�>�7�> �ރݳh41C��&�@K-�E&�rJCY�bd�A�(�18�^{0e!9�P�ה����UР�>y`���]Ǣ�,����߭��3�³,� �w����L?Ut/��{@3��PE�f#!�aH���ӧC��� %z�Ϡ���3, +��ZV�$&q��+�^����rO���:�Q�cRI�/���0-^��=4:���}Etl]8���\I��^N���`�9���ˀc*�c�!��^%���Z���[H�Դ�X�O �s�]��)�5=�x7N�I��KV�-"Ҧ����X��GC��Xnq�W08 ��>H0���P>݉��_�'�� �MOPnC�e�hx>c��pd9��K)2w"cf@<؃��Q�I��&ކ��ZÈ��MK�ͤ��)��A^ ӂK���>��P2��� |�߱Á�Cx��p2�:����r�b��! !�G�2���D�QA@��P�5M�C)���^0Dq:������=�iEG���gh��6r�c�p��ߐ�$ ������`#=���Xu��ؙ�tx�Vag(D+�8�(ʑ������X���$�6RͶU(��I,�ۓ�5r(��+�V{��{Ƨ9�l���h����KQ�R�-�J �S�.,(������ ��V�|��� ��-�H���+�e�G�W��)l$8="���{�_�������c�H +�¡�e�mʼ?]}�7�?;�GӳոnW����j�����R�/����3�]�MI��}��TNEK‡�B�Cy|jt눢� `�⚓1%�1c�hҺ���}N�� X�C,t^Pj��E�a'��'�r���!��Q�(��~6[���%�z��~���������֋�_6���Ո7`��ˬ=��H�{^ҏN�w�z�n�Bã�ŭ+���q��7�!b����= ��&�p�1���H��s�����Xl��M�K�%��/-�� ��hx>��Ӕ�>R��i�ᤗ��5������Ƚ'����TZ���8�ʘ�\nH�~UM��3�Kbx�Wl�ev�i�� *��00������|4��1r�ԕ��Eʥ������޻�Wҭ yJ�y��`j��� �1�T_��.Ԝ׿r:xk�hR�w��S�u����2�W�l��6���fbp\Äa���pq��~�T�������/�}�߉��~)ɢ��~�4~�4��P�e�(���л#���>�������-��=�ku3�m���>�z�Ɣ:ti�nc'u��nr�;=� +Q ���J*�:j��=���~��w�ӈtF$?[w�*iL7��z��_��i���ԩG�I��S�����#���qr��NO�G�Uս���4�T�����Q{4I��pkYu��G���0Z���� ��C�q���֋�����6�ڵ� ���0A��HCs�=[3��������x࿵ endstream endobj 1064 0 obj @@ -25597,7 +25591,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] -/Rect [448.987 598.695 470.07 614.845] +/Rect [184.541 586.573 205.624 599.534] /A << /S /GoTo /D (figure.caption.95) >> >> endobj @@ -25606,7 +25600,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] -/Rect [298.532 203.692 311.433 217.794] +/Rect [298.532 196.918 311.433 211.019] /A << /S /GoTo /D (cite.0@pip-tones) >> >> endobj @@ -25615,7 +25609,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] -/Rect [283.546 169.819 304.629 182.781] +/Rect [283.546 163.045 304.629 176.006] /A << /S /GoTo /D (figure.caption.145) >> >> endobj @@ -25624,7 +25618,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] -/Rect [483.618 133.61 505.28 150.669] +/Rect [483.618 126.835 505.28 143.894] /A << /S /GoTo /D (figure.caption.95) >> >> endobj @@ -25633,7 +25627,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] -/Rect [121.817 118.169 143.479 134.606] +/Rect [121.817 111.394 143.479 127.832] /A << /S /GoTo /D (figure.caption.145) >> >> endobj @@ -25649,122 +25643,122 @@ endobj endobj 638 0 obj << -/D [1064 0 R /XYZ 85.039 552.777 null] +/D [1064 0 R /XYZ 85.039 542.069 null] >> endobj 1067 0 obj << -/D [1064 0 R /XYZ 85.039 518.107 null] +/D [1064 0 R /XYZ 85.039 507.399 null] >> endobj 1068 0 obj << -/D [1064 0 R /XYZ 102.972 521.394 null] +/D [1064 0 R /XYZ 102.972 510.687 null] >> endobj 1069 0 obj << -/D [1064 0 R /XYZ 102.972 510.435 null] +/D [1064 0 R /XYZ 102.972 499.728 null] >> endobj 1070 0 obj << -/D [1064 0 R /XYZ 102.972 499.477 null] +/D [1064 0 R /XYZ 102.972 488.769 null] >> endobj 1071 0 obj << -/D [1064 0 R /XYZ 102.972 488.518 null] +/D [1064 0 R /XYZ 102.972 477.81 null] >> endobj 1072 0 obj << -/D [1064 0 R /XYZ 102.972 477.559 null] +/D [1064 0 R /XYZ 102.972 466.851 null] >> endobj 1073 0 obj << -/D [1064 0 R /XYZ 102.972 466.6 null] +/D [1064 0 R /XYZ 102.972 455.892 null] >> endobj 1074 0 obj << -/D [1064 0 R /XYZ 102.972 455.641 null] +/D [1064 0 R /XYZ 102.972 444.934 null] >> endobj 1075 0 obj << -/D [1064 0 R /XYZ 102.972 444.682 null] +/D [1064 0 R /XYZ 102.972 433.975 null] >> endobj 1076 0 obj << -/D [1064 0 R /XYZ 102.972 433.723 null] +/D [1064 0 R /XYZ 102.972 423.016 null] >> endobj 1077 0 obj << -/D [1064 0 R /XYZ 102.972 422.764 null] +/D [1064 0 R /XYZ 102.972 412.057 null] >> endobj 1078 0 obj << -/D [1064 0 R /XYZ 102.972 411.805 null] +/D [1064 0 R /XYZ 102.972 401.098 null] >> endobj 1079 0 obj << -/D [1064 0 R /XYZ 102.972 400.846 null] +/D [1064 0 R /XYZ 102.972 390.139 null] >> endobj 1080 0 obj << -/D [1064 0 R /XYZ 102.972 389.888 null] +/D [1064 0 R /XYZ 102.972 379.18 null] >> endobj 1081 0 obj << -/D [1064 0 R /XYZ 102.972 378.929 null] +/D [1064 0 R /XYZ 102.972 368.221 null] >> endobj 1082 0 obj << -/D [1064 0 R /XYZ 102.972 367.97 null] +/D [1064 0 R /XYZ 102.972 357.262 null] >> endobj 1083 0 obj << -/D [1064 0 R /XYZ 102.972 357.011 null] +/D [1064 0 R /XYZ 102.972 346.303 null] >> endobj 1084 0 obj << -/D [1064 0 R /XYZ 102.972 346.052 null] +/D [1064 0 R /XYZ 102.972 335.345 null] >> endobj 1085 0 obj << -/D [1064 0 R /XYZ 102.972 335.093 null] +/D [1064 0 R /XYZ 102.972 324.386 null] >> endobj 1086 0 obj << -/D [1064 0 R /XYZ 102.972 324.134 null] +/D [1064 0 R /XYZ 102.972 313.427 null] >> endobj 1087 0 obj << -/D [1064 0 R /XYZ 102.972 313.175 null] +/D [1064 0 R /XYZ 102.972 302.468 null] >> endobj 1088 0 obj << -/D [1064 0 R /XYZ 102.972 302.216 null] +/D [1064 0 R /XYZ 102.972 291.509 null] >> endobj 226 0 obj << -/D [1064 0 R /XYZ 85.039 260.184 null] +/D [1064 0 R /XYZ 85.039 256.251 null] >> endobj 1063 0 obj @@ -54790,9 +54784,9 @@ endobj /ProcSet [ /PDF /Text /ImageC ] >> endobj -1535 0 obj +1534 0 obj << -/Length 1943 +/Length 1940 /Filter /FlateDecode >> stream @@ -54803,14 +54797,15 @@ xڭX_ m�ԛfg�!�Q���Cʌ5�������K��%c2RTv�ՀI��GnH�/����f�9����|��(-4V�α�`)ɂ�n�B�`U�}�a�2^Y,�H����}�o������e:��p嘋�+��G,G����H����frg�כ�s���1T|�|�bo€;p�g�`J�I��T�� �cX��9 ѾSH�iQP/\��*b�_vR&?upA�\��V˸�r���7�s�Y��r0�ŇP��L���S�%g�E�ʭ��dwf�rwW���(9+��QL� M�3����J&�&Y���E�oE5�Q���E>U�ݜ`�-�s���Rap�W�{wm�ZYY�����K*+K�*o���o�VY�=m�>���Q�)h>����)��0�eWű�j�,��&��@��(l�A�b������Y[Bn�d�i�U/dlY1��)Y%b�u�.�6���ҝ��F�N��N����j�`�5��k���IT�� -�^1 �Lbb�R��b���T�Ⱥ�n+�����8�`%���5��$D��ex�"���S�Hp �X��J%��B)����rX��`�aS�f全(�;,�(d��KQB�AU�'�������?y��O��E�z +�^1 �Lbb�R���`Pjd�W��@�I]HX��Wш��{e +���2����{*� ��iض�~�TR����'gpuX���3Y�Ԭ��> @@ -55141,7 +55136,7 @@ V! ������3;� endstream endobj -1538 0 obj +1537 0 obj << /Type /XObject /Subtype /Image @@ -55237,32 +55232,32 @@ endobj /A << /S /GoTo /D (cite.0@icm7555) >> >> endobj -1536 0 obj +1535 0 obj << -/D [1534 0 R /XYZ 84.039 750.48 null] +/D [1533 0 R /XYZ 84.039 750.48 null] >> endobj 314 0 obj << -/D [1534 0 R /XYZ 85.039 710.898 null] +/D [1533 0 R /XYZ 85.039 710.898 null] >> endobj 318 0 obj << -/D [1534 0 R /XYZ 85.039 648.143 null] +/D [1533 0 R /XYZ 85.039 648.143 null] >> endobj 669 0 obj << -/D [1534 0 R /XYZ 85.039 391.004 null] +/D [1533 0 R /XYZ 85.039 391.004 null] >> endobj -1537 0 obj +1536 0 obj << -/D [1534 0 R /XYZ 268.274 145.915 null] +/D [1533 0 R /XYZ 268.274 127.609 null] >> endobj -1533 0 obj +1532 0 obj << /Font << /F98 727 0 R /F58 408 0 R /F61 409 0 R /F79 566 0 R /F62 410 0 R /F92 705 0 R >> /XObject << /Im33 1527 0 R >> @@ -55275,20 +55270,15 @@ endobj /Filter /FlateDecode >> stream -xڽYKs����W���� ������[l�r�}APD- � ������P���Um���g���둌�"}�����۫���2RRd2S���T�*�RcE�f��:�[{�������R�L�r����m~����B;'�z��nK���d|��v_u� -��k|����շ+�����bRX\��_}�*�5&��0z�K��U�Ж.��>]�� ]�v�Jd��)�I����a��^()e|s,�w��jX&s��h����2ϾI��9G�e�U{�Qj㿵T~�Hk?�a�{�s�j6��﫻>P�$4]_ -i�!#pI��K�?\���@�SC��6Q:�w�PB�����:�S�œ��d��^;���}QS%ϐ���o��':�x�I�{�`M ��lQ���CU�䰉�߼.I�ڥ^��]yG�ҽƧ3�C�}^�=�e�K7M��u��E7l���0F���@�/�o���fi�5�bo�D��χ��̙m���&Qb���2pf?�D�\�4&Mt,� y��ԇ�s�{6�At)d���>^�X��$�)a�������&@kƤ�V�r�A.��� ֑�n�s��mܖ���w�=,ʖ|�N��U���\��y�T6J��> 7�W� -.$�Ҁ�N-�p�s�k�NF�y&Oi"�R�2Oi&\��x�O�5dL�ʓ�Z{ƒ��X�Z ��WfI;���'��R!G�R��_6�|�� �p�Bช��<� �)k��eX� ��� �0�����!砵 - �W�����O�������[��LXg��0�����,�Sd _�T�%��G)Xr -�������6��� -�Ҧsa,�a7�2�j���&���[_��#oi6��S7�� A��"��b�Lt��pV�F#h|?J;Y*���_:X���(:����?�ߟƍ'�@�J�;X�$��_��N�q��w�����Bw?xs��M��s�n��{� �"�@�4/���$lPd�_��@&�M�ٷ?���� r��p�b8k19�w�J�vt�n�j��} 8��"ȧ ̎ B'l~���j�OU�;~4w��57=��uەy��'��N@h�� �rS�����"?�E���F0 #��" 3���( �Iw�k�!x��l�h0��m�6P/��X�<�1 {!��~��_=� �Y�������K��_���[�M���3$��*�� �V�.D�ᬨ#7Jӡ�m�v,Gχ%�e8��ď�M�"Y�Hg�������N5���A��<<��{��tU?�"� -g���TC@h�1u� ,�P��fO -��Tf�|ک�/R��(<��}^�J�UU�����sUo�Z��N��R��� m�ӨW�!�=��"�/W��Zd��˔u`F͙�y1S �He S�5+�! �&�߅A���������۞]���)r����\�H"j�� �h@�|�8W����>�����^�̾�u��� =��M�����:j��r��m��Rb��"�4Ke��eQх -�}*�,<*Qc*�7|�u�i �T͇�߅ U] �?� -D�����p��I2y�u��� cyX9A�~`�p�]�2�M��z�Ā���c�H�^�s�ݏ79l[J��k��H������E�W>L�E�Y����r��:���2�sE��-4�� -4$�f����uy��ux�Ð/l.�;����] ��]6���"��W��ĿV� I�,��C:�HRk�"Iݬ@ED�cm�k�;7d ���䩑����嚛�n��e� Og';gOY��L*˃�O�4xϷ~�d!6=��M�A,��c�["���O&>��>�� q�TW#����rRT�>N��챙��B� -_�c*B{*qܩె����@�i�P�Є�C@;� aNQ��Q8�~�����^�����§fؙBj -;��n]|���^�qoE`��|xa�(�.,�6�xC��Q�΋���:�c�h���kڶ -m�!�iO�/�u1IC�^��_���EC��Ы<釆�m���n����b����¾�/��b��K�����Ň�o��E���� �}*�?�������R"� +xڽYKs�8��W�tU�����)�dgn�xkIM[��HE$����� R�����T�D�4�����"}�����닷�]))r�����T�*�RcE����M�9�����/o�狩:K�r����M����J;'�r�_o*&\_�2>M��{�~�1������ۅBSFjv0),R�.>�� ��0 =���Ȫ\hK�F�.��;K%r��\�$�En�؉0B_���2�:TE_7w,ͻ�nY'K��h����2/�I��9G�,���(���:�/����۾��xn;���灲h���U����0�6�ա�n�۷<��)��p���0�~���Y��i��/� �D����}{���L�c��p�p����Z���_I*�����E��(��_;��M��2���� � ����n� MǗBg� \�G��㇋S��w� �o���Tz ;��֩�<5�)��RK�m�-ڞ�3��8T�)�n�v�x�Ljg�ą7�TP<��U����UY�6�Y��ɛ���]��Mߵ4�[|>�=�=��Y�CU5<��L��;��DžM�A���]� �� 5s�,��ֶ!�O�J�|8�-��&�Hl%� �,�`�J$�EJc�D�*���]2��0V�bϦR�!�fB&)+�㹃Or�֘1���IN�l��f�Lj�E�g,��Z�ۢdY��7<6���]�/E�Xê�x���XW[��Ǖ ���Q�L��Q�)�B�T�!�I�V8���h� +�L�}�L��D�L��Li.\�Nd��L�5�L�*��Z{"���D�Z ��WI;�dv"�}�L��N���?[K>��F8-!\�QM ��Д5'y�*��Bz&��bÀ!PQy}]@7���a�JS�\���e��#�rgv�P�s+���L� #�\ +X��9E��1�H���:$J!�4�K`��6��.7EsWeh)2�.��}�f\�PTL��F�����C]Zȭ��u ��QF9�=��������0��/A +� �|�QI�m��򑗴���S?�� ���,��j�Lt��pV�F#h|?i;y*���_:z���(:����?�ߟ��G�@�JM+x�$��_��N�q��w���P��BwO�:�x!&��)b7�>�d�L!PCs�Sfm?���%0� k�q���;���GD?n��Z�6��ۍI��r^)﶐����W�sJ)�|����!t��]�+`f��e�%�Gc�Ŷ���4����� |�}_�$��M}K����ٚ�kY싲^��6 &!o�ם�aF}є�9�N��6�<�} �Qtm��UW굇3&�0�����Kӧq�u�@����� �Y�C�M�VΑ�| +������x׎W-JoW�1D��ŭ��(MǛn�v�]p +}��P��8K�x�čQ$��4�?�W�Ze�9@�<�~/����j�^5��Ƞ™악J��m ���V�9@���M}��۸�w�拔��{���+���v]7��uLy���`%�4i�^GK��#��L�4�/� I�ڗ��XX��u��&WK��^,�!2�j-���FCS�o�0��M����7�ڢǧM�ݠ_��=fh*N��5fu�[�C���مz�\\$Wy*pWX�,IL�Ӣo�F ��{Ncu���� h�oS���m_�}��#�n�6(�a����:L�T1��o�<�,�O�ɼJ��eY�yJ}��<�*Qc��7|����F�V@<�a�M9���=������Y8R���\U@]z��)��3g���-���ƱI� �`�t(�iV�s +�v��$��cG�{]����{� ������H3��� +ؾ��=�P��5A�L��+0��m\`Ҡz�H�d��/j,o�H�jsN����D�rt^��'w)B:E}����j��� +~� ��o��Z�I�_\p'��.���]z2�5{l$�R���f��j�������9TV'"� ��c8���ms9��MO�~�]PKNx�P�.��Px(�� HR� �E�8��Y#����rv�������s3��B�_>S-B{�q8���c ?[��#�� ��� c�w�Ҝ�4�p8�B�M� �ø�i�?���4�3�Ԕw8��2��.��Y7ƍU�!��A*��T��>L۴��tBo�ɲΫ����c�j���k��m�#"h����u9+C������i�"��&�.O�!r���=7����k ڿ�}X���W�تӻ��?�Q��Ä�I6��r��܅ѡ W�?�'�����ߣ#� endstream endobj 1550 0 obj @@ -55302,7 +55292,7 @@ endobj /Annots [ 1530 0 R 1531 0 R 1541 0 R 1542 0 R 1543 0 R ] >> endobj -1532 0 obj +1540 0 obj << /Type /XObject /Subtype /Image @@ -55486,7 +55476,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] -/Rect [467.159 335.867 490.97 348.828] +/Rect [467.159 342.641 490.97 355.603] /A << /S /GoTo /D (section*.441) >> >> endobj @@ -55495,7 +55485,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] -/Rect [378.195 94.777 391.096 107.739] +/Rect [378.195 101.552 391.096 114.513] /A << /S /GoTo /D (cite.0@amazon-resistors) >> >> endobj @@ -55504,7 +55494,7 @@ endobj /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] -/Rect [400.276 94.777 413.178 107.739] +/Rect [400.276 101.552 413.178 114.513] /A << /S /GoTo /D (cite.0@amazon-capacitors) >> >> endobj @@ -55525,13 +55515,13 @@ endobj endobj 326 0 obj << -/D [1550 0 R /XYZ 107.717 309.762 null] +/D [1550 0 R /XYZ 107.717 316.537 null] >> endobj 1549 0 obj << /Font << /F58 408 0 R /F98 727 0 R /F61 409 0 R /F79 566 0 R /F93 706 0 R /F62 410 0 R >> -/XObject << /Im34 1532 0 R >> +/XObject << /Im34 1540 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj @@ -55825,15 +55815,15 @@ endobj endobj 1575 0 obj << -/Length 1016 +/Length 1007 /Filter /FlateDecode >> stream -xڽVK��6��W�(�oJ=��.6��[=ls�e� K�E����CZ�q�l�(`X�p8�of�q�!��$~߬7�2C������0�XS�4X��6�1j�q���6���La*X�J�TEo�a�2I���T$YU&V˜$����uZ`or㟾�V�O -C��c p��/?�����`��W�#As̄s�A�/~�ʮ�����\(v[�9�y���X����z�lg���݅H̃F`���}.�Lj[�6��&CY�}a�2�7��Ԇ�:~o�0�K�%&�3)r�G���T9:�k�z��Gq�n.}�MY�E�N}.���Z��.�ҙq����,C�q̴@JS�8w� 0�Q����`�֥dn�<1Ptg�La9RZ`�X,�k�E���`�k��".�_�|�݀�*l聾�-S�{!)����I)L�����)��U�+c�2������t_nb&�a���Ga� T���h����Ә���78}_��� о'4���l��c"/��GS��c�Қ/69X�G'��]X9��/5����V������î *E��öb�� -?�Lv�+�~�;g(�;> e�c�4N�3ʼnH��i�ˢ ��+u�6�_ٵ��-��B�W�;@au� 8�Pu�!8Ե���XVat �S=�̓i"~�ˈ���`��� �%M�6M�A]ڸ�FSL�:%�š�]��/��C�lqx - -���ک��a � g;L���W�na�t���1;v*P6���va�i����`ֵ���zW�0�ͨ��К���A��A��-w�8�?��U_o�&D�\!��y6���e�Xr�������ݴ��>�S�N/����$�����5.�7�n�t�QȌk�� ��Db"�����S�a��*��p6�K�P�g�N�Q|s�����[��4����/�-%�/������Clˌ-��"c�f�9]�f8D�x�!�c�Mp����l�c��ui���p�qc;�1�����4��n +xڽVߓ�6~�_�Go�]=:-�7��O�w�ŧ�~�� +)v�Ǐ��0��0*a��UwD� +�k��߾��a0��TF��6YF3`��1g�E�����b$�Ac0R ���?�ZG�X!�C�����.���}~lq���%���~���b���*|Lj�`j��=��6�p� �ސ�r�0���O�-����ys��u��B�������eH IE��I85R� � ��pA��do�ƥdn�<1Ptg�U�P92��F�Pj� 6.s��2s�џ^�H��~(�����$�Pa3@���l��� �HʌzeL�Pu8�$�7L��J_S���$��`�����M̤S�R�:�8� ��6��Dp�%������N�խu��� ���8�w�� 6��42����ˈ[��~�g�upŏ��v�4V������G�kQ%��5.��u�{���W@��w&H�%u(|@��f��$�Δd*ꫧ�.�{��u���k ۏpo�D�B�����k� UwС� ��B�\�z�7��.�. �뀽��`��i��k�4���I�"���ĩM�wB��t#�1�?��ۢgit�j3��I�b �{��O� g�{��?s���-��-^�g�];�3����Ո�P�'�ͅ�=z���� z w9��$�?�1ܡ +���:��f �hV��Cw��������~��w��g��4q>]��3Q�F�g��ћk��]��iq=������Je@F@I�J��ӂr��D?%��z}���Ʉ��̞���q�!>��_ZX�$�\ ������{�1������X�a��t}���B#+> endobj -1539 0 obj +1538 0 obj << /D [1694 0 R /XYZ 85.039 521.375 null] >> endobj -1540 0 obj +1539 0 obj << /D [1694 0 R /XYZ 85.039 494.277 null] >> @@ -72823,7 +72813,7 @@ endobj /Type /Pages /Count 6 /Parent 1737 0 R -/Kids [1509 0 R 1516 0 R 1534 0 R 1550 0 R 1561 0 R 1574 0 R] +/Kids [1509 0 R 1516 0 R 1533 0 R 1550 0 R 1561 0 R 1574 0 R] >> endobj 1582 0 obj @@ -73852,7 +73842,7 @@ endobj endobj 1750 0 obj << -/Names [(cite.0@gocube-rubiksconnected) 819 0 R (cite.0@googleplay-spectroid) 916 0 R (cite.0@googleplay-twistytimer) 875 0 R (cite.0@hackrubik) 834 0 R (cite.0@harmonics) 1539 0 R (cite.0@ibm-cv-definition) 833 0 R] +/Names [(cite.0@gocube-rubiksconnected) 819 0 R (cite.0@googleplay-spectroid) 916 0 R (cite.0@googleplay-twistytimer) 875 0 R (cite.0@hackrubik) 834 0 R (cite.0@harmonics) 1538 0 R (cite.0@ibm-cv-definition) 833 0 R] /Limits [(cite.0@gocube-rubiksconnected) (cite.0@ibm-cv-definition)] >> endobj @@ -73876,7 +73866,7 @@ endobj endobj 1754 0 obj << -/Names [(cite.0@speedsolving-com) 876 0 R (cite.0@square-waves) 1540 0 R (cite.0@typical-mic-range) 634 0 R (cite.0@wca-regulations) 762 0 R (equation.438) 1537 0 R (figure.caption.121) 638 0 R] +/Names [(cite.0@speedsolving-com) 876 0 R (cite.0@square-waves) 1539 0 R (cite.0@typical-mic-range) 634 0 R (cite.0@wca-regulations) 762 0 R (equation.438) 1536 0 R (figure.caption.121) 638 0 R] /Limits [(cite.0@speedsolving-com) (figure.caption.121)] >> endobj @@ -74272,7 +74262,7 @@ endobj endobj 1820 0 obj << -/Names [(page.66) 1495 0 R (page.67) 1511 0 R (page.68) 1518 0 R (page.69) 1536 0 R (page.7) 438 0 R (page.70) 1552 0 R] +/Names [(page.66) 1495 0 R (page.67) 1511 0 R (page.68) 1518 0 R (page.69) 1535 0 R (page.7) 438 0 R (page.70) 1552 0 R] /Limits [(page.66) (page.70)] >> endobj @@ -74535,8 +74525,8 @@ endobj << /Producer (MiKTeX pdfTeX-1.40.21) /Author(\376\377\000J\000o\000s\000e\000p\000h\000\040\000H\000a\000l\000e)/Title(\376\377\000D\000I\000Y\000\040\000S\000u\000p\000e\000r\000c\000u\000b\000e)/Subject()/Creator(LaTeX with hyperref)/Keywords() -/CreationDate (D:20211123124454-07'00') -/ModDate (D:20211123124454-07'00') +/CreationDate (D:20211124114233-07'00') +/ModDate (D:20211124114233-07'00') /Trapped /False /PTEX.Fullbanner (This is MiKTeX-pdfTeX 2.9.7338 (1.40.21)) >> @@ -74546,399 +74536,399 @@ xref 0000000781 65535 f 0000000015 00000 n 0000023888 00000 n -0017758051 00000 n +0017758060 00000 n 0000000061 00000 n 0000000218 00000 n 0000026667 00000 n -0017757966 00000 n +0017757975 00000 n 0000000264 00000 n 0000000330 00000 n 0000027635 00000 n -0017757879 00000 n +0017757888 00000 n 0000000376 00000 n 0000000483 00000 n -0000068627 00000 n -0017757753 00000 n +0000068639 00000 n +0017757762 00000 n 0000000530 00000 n 0000000617 00000 n -0000068687 00000 n -0017757679 00000 n +0000068699 00000 n +0017757688 00000 n 0000000664 00000 n 0000000828 00000 n -0000068747 00000 n -0017757592 00000 n +0000068759 00000 n +0017757601 00000 n 0000000875 00000 n 0000001013 00000 n -0000070042 00000 n -0017757505 00000 n +0000070054 00000 n +0017757514 00000 n 0000001060 00000 n 0000001206 00000 n -0000070103 00000 n -0017757431 00000 n +0000070115 00000 n +0017757440 00000 n 0000001253 00000 n 0000001358 00000 n -0000073346 00000 n -0017757305 00000 n +0000073358 00000 n +0017757314 00000 n 0000001405 00000 n 0000001482 00000 n -0000073406 00000 n -0017757231 00000 n +0000073418 00000 n +0017757240 00000 n 0000001529 00000 n 0000001749 00000 n -0000073466 00000 n -0017757107 00000 n +0000073478 00000 n +0017757116 00000 n 0000001796 00000 n 0000001983 00000 n -0000075210 00000 n -0017757033 00000 n +0000075222 00000 n +0017757042 00000 n 0000002033 00000 n 0000002153 00000 n -0000075271 00000 n -0017756959 00000 n +0000075283 00000 n +0017756968 00000 n 0000002203 00000 n 0000002342 00000 n -0000075332 00000 n -0017756848 00000 n +0000075344 00000 n +0017756857 00000 n 0000002389 00000 n 0000002476 00000 n -0000075393 00000 n -0017756774 00000 n +0000075405 00000 n +0017756783 00000 n 0000002526 00000 n 0000002680 00000 n -0000075453 00000 n -0017756663 00000 n +0000075465 00000 n +0017756672 00000 n 0000002730 00000 n 0000002817 00000 n -0000075514 00000 n -0017756589 00000 n +0000075526 00000 n +0017756598 00000 n 0000002865 00000 n 0000003031 00000 n -0000075575 00000 n -0017756515 00000 n +0000075587 00000 n +0017756524 00000 n 0000003079 00000 n 0000003224 00000 n -0000079009 00000 n -0017756387 00000 n +0000079021 00000 n +0017756396 00000 n 0000003271 00000 n 0000003387 00000 n -0000079069 00000 n -0017756313 00000 n +0000079081 00000 n +0017756322 00000 n 0000003434 00000 n 0000003521 00000 n -0000079129 00000 n -0017756189 00000 n +0000079141 00000 n +0017756198 00000 n 0000003568 00000 n 0000003703 00000 n -0000079188 00000 n -0017756115 00000 n +0000079200 00000 n +0017756124 00000 n 0000003753 00000 n 0000003838 00000 n -0002273077 00000 n -0017756028 00000 n +0002273089 00000 n +0017756037 00000 n 0000003888 00000 n 0000003953 00000 n -0002273200 00000 n -0017755954 00000 n +0002273212 00000 n +0017755963 00000 n 0000004003 00000 n 0000004078 00000 n -0002705691 00000 n -0017755826 00000 n +0002705703 00000 n +0017755835 00000 n 0000004125 00000 n 0000004193 00000 n -0002705751 00000 n -0017755709 00000 n +0002705763 00000 n +0017755718 00000 n 0000004244 00000 n 0000004350 00000 n -0002705812 00000 n -0017755630 00000 n +0002705824 00000 n +0017755639 00000 n 0000004399 00000 n 0000004573 00000 n -0002705873 00000 n -0017755537 00000 n +0002705885 00000 n +0017755546 00000 n 0000004622 00000 n 0000004845 00000 n -0003393832 00000 n -0017755458 00000 n +0003393844 00000 n +0017755467 00000 n 0000004894 00000 n 0000005223 00000 n -0003393894 00000 n -0017755366 00000 n +0003393906 00000 n +0017755375 00000 n 0000005274 00000 n 0000005395 00000 n -0003398369 00000 n -0017755288 00000 n +0003398381 00000 n +0017755297 00000 n 0000005446 00000 n 0000005592 00000 n -0003398430 00000 n -0017755158 00000 n +0003398442 00000 n +0017755167 00000 n 0000005640 00000 n 0000005789 00000 n -0003398491 00000 n -0017755079 00000 n +0003398503 00000 n +0017755088 00000 n 0000005840 00000 n 0000005893 00000 n -0003398552 00000 n -0017754986 00000 n +0003398564 00000 n +0017754995 00000 n 0000005944 00000 n 0000006172 00000 n -0003511676 00000 n -0017754907 00000 n +0003511688 00000 n +0017754916 00000 n 0000006223 00000 n 0000006415 00000 n -0003511800 00000 n -0017754829 00000 n +0003511812 00000 n +0017754838 00000 n 0000006463 00000 n 0000006584 00000 n -0003517512 00000 n -0017754697 00000 n +0003517524 00000 n +0017754706 00000 n 0000006632 00000 n 0000006723 00000 n -0003517573 00000 n -0017754618 00000 n +0003517585 00000 n +0017754627 00000 n 0000006771 00000 n 0000006859 00000 n -0003517634 00000 n -0017754486 00000 n +0003517646 00000 n +0017754495 00000 n 0000006907 00000 n 0000006995 00000 n -0003517695 00000 n -0017754368 00000 n +0003517707 00000 n +0017754377 00000 n 0000007046 00000 n 0000007182 00000 n -0003521153 00000 n -0017754289 00000 n +0003521165 00000 n +0017754298 00000 n 0000007231 00000 n 0000007396 00000 n -0003521215 00000 n -0017754210 00000 n +0003521227 00000 n +0017754219 00000 n 0000007445 00000 n 0000007670 00000 n -0003521277 00000 n -0017754117 00000 n +0003521289 00000 n +0017754126 00000 n 0000007721 00000 n 0000007852 00000 n -0004621327 00000 n -0017754024 00000 n +0004621339 00000 n +0017754033 00000 n 0000007903 00000 n 0000008171 00000 n -0004681755 00000 n -0017753945 00000 n +0004681767 00000 n +0017753954 00000 n 0000008222 00000 n 0000008356 00000 n -0004681817 00000 n -0017753813 00000 n +0004681829 00000 n +0017753822 00000 n 0000008404 00000 n 0000008497 00000 n -0004681878 00000 n -0017753734 00000 n +0004681890 00000 n +0017753743 00000 n 0000008548 00000 n 0000008773 00000 n -0004751245 00000 n -0017753641 00000 n +0004751257 00000 n +0017753650 00000 n 0000008824 00000 n 0000008953 00000 n -0004751306 00000 n -0017753562 00000 n +0004751318 00000 n +0017753571 00000 n 0000009004 00000 n 0000009186 00000 n -0004755165 00000 n -0017753469 00000 n +0004755177 00000 n +0017753478 00000 n 0000009234 00000 n 0000009365 00000 n -0004757804 00000 n -0017753390 00000 n +0004757816 00000 n +0017753399 00000 n 0000009413 00000 n 0000009476 00000 n -0004761791 00000 n -0017753257 00000 n +0004761805 00000 n +0017753266 00000 n 0000009524 00000 n 0000009615 00000 n -0004761853 00000 n -0017753178 00000 n +0004761867 00000 n +0017753187 00000 n 0000009663 00000 n 0000009751 00000 n -0004762167 00000 n -0017753046 00000 n +0004762181 00000 n +0017753055 00000 n 0000009799 00000 n 0000009953 00000 n -0004765376 00000 n -0017752967 00000 n +0004765390 00000 n +0017752976 00000 n 0000010004 00000 n 0000010196 00000 n -0004771725 00000 n -0017752874 00000 n +0004771743 00000 n +0017752883 00000 n 0000010248 00000 n 0000010430 00000 n -0004773253 00000 n -0017752795 00000 n +0004773272 00000 n +0017752804 00000 n 0000010482 00000 n 0000010783 00000 n -0005350101 00000 n -0017752663 00000 n +0005350120 00000 n +0017752672 00000 n 0000010832 00000 n 0000011009 00000 n -0005350163 00000 n -0017752545 00000 n +0005350182 00000 n +0017752554 00000 n 0000011061 00000 n 0000011220 00000 n -0005355535 00000 n -0017752480 00000 n +0005355554 00000 n +0017752489 00000 n 0000011270 00000 n 0000011587 00000 n -0005510463 00000 n -0017752387 00000 n +0005510482 00000 n +0017752396 00000 n 0000011639 00000 n 0000011904 00000 n -0005517338 00000 n -0017752294 00000 n +0005517357 00000 n +0017752303 00000 n 0000011956 00000 n 0000012274 00000 n -0005529455 00000 n -0017752201 00000 n +0005529474 00000 n +0017752210 00000 n 0000012326 00000 n 0000012662 00000 n -0005536872 00000 n -0017752122 00000 n +0005536891 00000 n +0017752131 00000 n 0000012714 00000 n 0000013015 00000 n -0006425234 00000 n -0017752029 00000 n +0006425253 00000 n +0017752038 00000 n 0000013064 00000 n 0000013218 00000 n -0006425360 00000 n -0017751897 00000 n +0006425379 00000 n +0017751906 00000 n 0000013267 00000 n 0000013421 00000 n -0007545013 00000 n -0017751818 00000 n +0007545032 00000 n +0017751827 00000 n 0000013473 00000 n 0000013632 00000 n -0008500452 00000 n -0017751725 00000 n +0008500471 00000 n +0017751734 00000 n 0000013684 00000 n 0000013934 00000 n -0008506920 00000 n -0017751632 00000 n +0008506939 00000 n +0017751641 00000 n 0000013986 00000 n 0000014254 00000 n -0008513953 00000 n -0017751553 00000 n +0008513972 00000 n +0017751562 00000 n 0000014306 00000 n 0000014495 00000 n -0008514015 00000 n -0017751474 00000 n +0008514034 00000 n +0017751483 00000 n 0000014544 00000 n 0000014607 00000 n -0008517628 00000 n -0017751341 00000 n +0008517647 00000 n +0017751350 00000 n 0000014656 00000 n 0000014762 00000 n -0008517690 00000 n -0017751262 00000 n +0008517709 00000 n +0017751271 00000 n 0000014811 00000 n 0000014899 00000 n -0008517752 00000 n -0017751130 00000 n +0008517771 00000 n +0017751139 00000 n 0000014948 00000 n 0000015036 00000 n -0008517814 00000 n -0017751051 00000 n +0008517833 00000 n +0017751060 00000 n 0000015088 00000 n 0000015262 00000 n -0012870997 00000 n -0017750958 00000 n +0012871016 00000 n +0017750967 00000 n 0000015314 00000 n 0000015491 00000 n -0012871060 00000 n -0017750865 00000 n +0012871079 00000 n +0017750874 00000 n 0000015543 00000 n 0000015720 00000 n -0012871123 00000 n -0017750786 00000 n +0012871142 00000 n +0017750795 00000 n 0000015772 00000 n 0000015908 00000 n -0012905052 00000 n -0017750654 00000 n +0012905068 00000 n +0017750663 00000 n 0000015957 00000 n 0000016015 00000 n -0012905114 00000 n -0017750575 00000 n +0012905130 00000 n +0017750584 00000 n 0000016067 00000 n 0000016166 00000 n -0012948543 00000 n -0017750457 00000 n +0012948561 00000 n +0017750466 00000 n 0000016218 00000 n 0000016319 00000 n -0012948669 00000 n -0017750392 00000 n +0012948687 00000 n +0017750401 00000 n 0000016369 00000 n 0000017161 00000 n -0017541076 00000 n -0017750299 00000 n +0017541085 00000 n +0017750308 00000 n 0000017210 00000 n 0000017293 00000 n -0017542184 00000 n -0017750206 00000 n +0017542193 00000 n +0017750215 00000 n 0000017342 00000 n 0000017445 00000 n -0017542245 00000 n -0017750113 00000 n +0017542254 00000 n +0017750122 00000 n 0000017494 00000 n 0000017668 00000 n -0017542307 00000 n -0017750034 00000 n +0017542316 00000 n +0017750043 00000 n 0000017717 00000 n 0000017780 00000 n -0017544044 00000 n -0017749901 00000 n +0017544053 00000 n +0017749910 00000 n 0000017829 00000 n 0000017907 00000 n -0017544106 00000 n -0017749822 00000 n +0017544115 00000 n +0017749831 00000 n 0000017956 00000 n 0000018183 00000 n -0017544294 00000 n -0017749729 00000 n +0017544303 00000 n +0017749738 00000 n 0000018232 00000 n 0000018376 00000 n -0017544356 00000 n -0017749636 00000 n +0017544365 00000 n +0017749645 00000 n 0000018425 00000 n 0000018584 00000 n -0017544418 00000 n -0017749557 00000 n +0017544427 00000 n +0017749566 00000 n 0000018633 00000 n 0000018764 00000 n -0017546244 00000 n -0017749424 00000 n +0017546253 00000 n +0017749433 00000 n 0000018813 00000 n 0000018891 00000 n -0017546306 00000 n -0017749345 00000 n +0017546315 00000 n +0017749354 00000 n 0000018940 00000 n 0000019003 00000 n -0017546368 00000 n -0017749252 00000 n +0017546377 00000 n +0017749261 00000 n 0000019052 00000 n 0000019135 00000 n -0017546430 00000 n -0017749173 00000 n +0017546439 00000 n +0017749182 00000 n 0000019184 00000 n 0000019346 00000 n -0017548854 00000 n -0017749040 00000 n +0017548863 00000 n +0017749049 00000 n 0000019396 00000 n 0000019530 00000 n -0017548916 00000 n -0017748961 00000 n +0017548925 00000 n +0017748970 00000 n 0000019579 00000 n 0000019804 00000 n -0017551027 00000 n -0017748882 00000 n +0017551036 00000 n +0017748891 00000 n 0000019853 00000 n 0000020078 00000 n -0017561617 00000 n -0017748802 00000 n +0017561626 00000 n +0017748811 00000 n 0000020129 00000 n 0000020217 00000 n 0000021064 00000 n @@ -74951,10 +74941,10 @@ xref 0000020268 00000 n 0000022118 00000 n 0000022178 00000 n -0017746088 00000 n -0017745913 00000 n -0017746263 00000 n -0017746623 00000 n +0017746097 00000 n +0017745922 00000 n +0017746272 00000 n +0017746632 00000 n 0000022610 00000 n 0000022437 00000 n 0000022337 00000 n @@ -74982,7 +74972,7 @@ xref 0000025936 00000 n 0000025319 00000 n 0000026607 00000 n -0017746741 00000 n +0017746750 00000 n 0000027097 00000 n 0000026924 00000 n 0000026824 00000 n @@ -75109,10 +75099,10 @@ xref 0000045230 00000 n 0000044271 00000 n 0000048926 00000 n -0017745442 00000 n -0017746859 00000 n -0005350225 00000 n -0005510525 00000 n +0017745451 00000 n +0017746868 00000 n +0005350244 00000 n +0005510544 00000 n 0000049357 00000 n 0000049184 00000 n 0000049084 00000 n @@ -75168,42 +75158,42 @@ xref 0000058622 00000 n 0000058682 00000 n 0000054930 00000 n -0002273015 00000 n -0002273138 00000 n -0017562297 00000 n -0002705630 00000 n -0003393956 00000 n -0003511738 00000 n -0004027852 00000 n -0004621266 00000 n -0004681693 00000 n -0017572339 00000 n -0004751184 00000 n -0017572528 00000 n -0004765439 00000 n -0004771787 00000 n -0004778407 00000 n -0004780324 00000 n -0005355598 00000 n -0005510587 00000 n -0005516828 00000 n -0005517401 00000 n -0005522857 00000 n -0005523236 00000 n -0005524383 00000 n -0005529518 00000 n -0005536238 00000 n -0005536934 00000 n -0006425297 00000 n -0007545075 00000 n -0007545137 00000 n -0008496220 00000 n -0008496474 00000 n -0008500514 00000 n -0008501277 00000 n -0008506983 00000 n -0008513255 00000 n -0012870934 00000 n +0002273027 00000 n +0002273150 00000 n +0017562306 00000 n +0002705642 00000 n +0003393968 00000 n +0003511750 00000 n +0004027864 00000 n +0004621278 00000 n +0004681705 00000 n +0017572348 00000 n +0004751196 00000 n +0017572537 00000 n +0004765453 00000 n +0004771805 00000 n +0004778426 00000 n +0004780343 00000 n +0005355617 00000 n +0005510606 00000 n +0005516847 00000 n +0005517420 00000 n +0005522876 00000 n +0005523255 00000 n +0005524402 00000 n +0005529537 00000 n +0005536257 00000 n +0005536953 00000 n +0006425316 00000 n +0007545094 00000 n +0007545156 00000 n +0008496239 00000 n +0008496493 00000 n +0008500533 00000 n +0008501296 00000 n +0008507002 00000 n +0008513274 00000 n +0012870953 00000 n 0000059908 00000 n 0000060069 00000 n 0000060229 00000 n @@ -75212,12 +75202,12 @@ xref 0000059418 00000 n 0000058828 00000 n 0000060551 00000 n -0012905176 00000 n -0017578975 00000 n -0012948606 00000 n -0013003488 00000 n -0013003550 00000 n -0017541139 00000 n +0012905192 00000 n +0017578984 00000 n +0012948624 00000 n +0013003506 00000 n +0013003568 00000 n +0017541148 00000 n 0000061221 00000 n 0000061380 00000 n 0000061660 00000 n @@ -75225,8 +75215,8 @@ xref 0000060697 00000 n 0000061539 00000 n 0000061599 00000 n -0000073526 00000 n -0004755103 00000 n +0000073538 00000 n +0004755115 00000 n 0000062018 00000 n 0000061845 00000 n 0000061745 00000 n @@ -75237,7 +75227,7 @@ xref 0000062590 00000 n 0000062650 00000 n 0000062711 00000 n -0017746977 00000 n +0017746986 00000 n 0000063129 00000 n 0000062956 00000 n 0000062856 00000 n @@ -75248,8 +75238,8 @@ xref 0000063671 00000 n 0000063731 00000 n 0000063792 00000 n -0017744987 00000 n -0017745131 00000 n +0017744996 00000 n +0017745140 00000 n 0000064248 00000 n 0000064075 00000 n 0000063975 00000 n @@ -75260,7 +75250,7 @@ xref 0000064789 00000 n 0000064849 00000 n 0000064910 00000 n -0017745757 00000 n +0017745766 00000 n 0000065366 00000 n 0000065193 00000 n 0000065093 00000 n @@ -75270,1148 +75260,1148 @@ xref 0000065407 00000 n 0000065743 00000 n 0000065803 00000 n -0017746438 00000 n -0017747095 00000 n +0017746447 00000 n +0017747104 00000 n 0000066222 00000 n 0000066049 00000 n 0000065949 00000 n 0000066161 00000 n -0000068412 00000 n -0000068869 00000 n -0000068280 00000 n +0000068424 00000 n +0000068881 00000 n +0000068292 00000 n 0000066263 00000 n -0000068567 00000 n -0000068807 00000 n -0000070164 00000 n -0000069869 00000 n -0000068967 00000 n -0000069981 00000 n -0000072464 00000 n -0000072630 00000 n -0000072791 00000 n -0000072959 00000 n -0000073131 00000 n -0000073648 00000 n -0000072300 00000 n -0000070275 00000 n -0000073286 00000 n -0000073587 00000 n -0017561679 00000 n -0017561740 00000 n -0017561802 00000 n -0017561864 00000 n -0000074985 00000 n -0000075636 00000 n -0000074853 00000 n -0000073746 00000 n -0000075149 00000 n -0017561926 00000 n -0000077707 00000 n -0000077860 00000 n -0000078014 00000 n -0000078167 00000 n -0000078322 00000 n -0000078477 00000 n -0000078630 00000 n -0000078783 00000 n -0002270625 00000 n -0002270785 00000 n -0002270945 00000 n -0002271105 00000 n -0000079248 00000 n -0000077519 00000 n -0000075734 00000 n -0000078949 00000 n -0017747213 00000 n -0017561988 00000 n +0000068579 00000 n +0000068819 00000 n +0000070176 00000 n +0000069881 00000 n +0000068979 00000 n +0000069993 00000 n +0000072476 00000 n +0000072642 00000 n +0000072803 00000 n +0000072971 00000 n +0000073143 00000 n +0000073660 00000 n +0000072312 00000 n +0000070287 00000 n +0000073298 00000 n +0000073599 00000 n +0017561688 00000 n +0017561749 00000 n +0017561811 00000 n +0017561873 00000 n +0000074997 00000 n +0000075648 00000 n +0000074865 00000 n +0000073758 00000 n +0000075161 00000 n +0017561935 00000 n +0000077719 00000 n +0000077872 00000 n +0000078026 00000 n +0000078179 00000 n +0000078334 00000 n +0000078489 00000 n +0000078642 00000 n +0000078795 00000 n +0002270637 00000 n +0002270797 00000 n +0002270957 00000 n +0002271117 00000 n +0000079260 00000 n +0000077531 00000 n +0000075746 00000 n +0000078961 00000 n +0017747222 00000 n +0017561997 00000 n 0000000785 00000 f -0002271270 00000 n -0000081764 00000 n -0000421602 00000 n +0002271282 00000 n +0000081776 00000 n +0000421614 00000 n 0000000788 00000 f -0002271436 00000 n -0000421673 00000 n +0002271448 00000 n +0000421685 00000 n 0000000791 00000 f -0002271609 00000 n -0000714492 00000 n +0002271621 00000 n +0000714504 00000 n 0000000798 00000 f -0002271782 00000 n -0000923877 00000 n -0002271955 00000 n -0002272131 00000 n -0002272291 00000 n -0002272451 00000 n +0002271794 00000 n +0000923889 00000 n +0002271967 00000 n +0002272143 00000 n +0002272303 00000 n +0002272463 00000 n 0000000821 00000 f -0002272622 00000 n -0001184918 00000 n -0001529909 00000 n -0001853160 00000 n -0002272788 00000 n -0002704916 00000 n -0002273261 00000 n -0000081513 00000 n -0000079333 00000 n -0002272954 00000 n -0000421245 00000 n -0000714135 00000 n -0000923520 00000 n -0001184561 00000 n -0001529552 00000 n -0001852803 00000 n -0002270113 00000 n -0017562050 00000 n -0017562112 00000 n -0017562173 00000 n -0017562235 00000 n -0017562359 00000 n +0002272634 00000 n +0001184930 00000 n +0001529921 00000 n +0001853172 00000 n +0002272800 00000 n +0002704928 00000 n +0002273273 00000 n +0000081525 00000 n +0000079345 00000 n +0002272966 00000 n +0000421257 00000 n +0000714147 00000 n +0000923532 00000 n +0001184573 00000 n +0001529564 00000 n +0001852815 00000 n +0002270125 00000 n +0017562059 00000 n +0017562121 00000 n +0017562182 00000 n +0017562244 00000 n +0017562368 00000 n 0000000840 00000 f -0002705076 00000 n -0002275495 00000 n -0002705244 00000 n -0002705411 00000 n -0003392469 00000 n -0002705934 00000 n -0002275324 00000 n -0002273473 00000 n -0002705570 00000 n -0002704559 00000 n -0017562421 00000 n -0017562482 00000 n -0017571457 00000 n -0003392635 00000 n -0003392797 00000 n -0003392950 00000 n -0003393109 00000 n -0003393268 00000 n +0002705088 00000 n +0002275507 00000 n +0002705256 00000 n +0002705423 00000 n +0003392481 00000 n +0002705946 00000 n +0002275336 00000 n +0002273485 00000 n +0002705582 00000 n +0002704571 00000 n +0017562430 00000 n +0017562491 00000 n +0017571466 00000 n +0003392647 00000 n +0003392809 00000 n +0003392962 00000 n +0003393121 00000 n +0003393280 00000 n 0000000843 00000 f -0003393443 00000 n -0002708642 00000 n +0003393455 00000 n +0002708654 00000 n 0000000877 00000 f -0003393597 00000 n -0002994280 00000 n -0003397007 00000 n -0003394018 00000 n -0002708439 00000 n -0002706068 00000 n -0003393771 00000 n -0002993470 00000 n -0003391918 00000 n -0017571520 00000 n -0017571583 00000 n -0017571646 00000 n -0017571709 00000 n -0017571772 00000 n -0003397163 00000 n -0003397324 00000 n -0003397972 00000 n -0003398144 00000 n -0003397478 00000 n -0003397647 00000 n -0003397804 00000 n -0003510962 00000 n -0003511126 00000 n -0003398675 00000 n -0003396819 00000 n -0003394179 00000 n -0003398309 00000 n -0003398613 00000 n -0017571835 00000 n -0017572024 00000 n -0017572087 00000 n -0017571898 00000 n -0017571961 00000 n +0003393609 00000 n +0002994292 00000 n +0003397019 00000 n +0003394030 00000 n +0002708451 00000 n +0002706080 00000 n +0003393783 00000 n +0002993482 00000 n +0003391930 00000 n +0017571529 00000 n +0017571592 00000 n +0017571655 00000 n +0017571718 00000 n +0017571781 00000 n +0003397175 00000 n +0003397336 00000 n +0003397984 00000 n +0003398156 00000 n +0003397490 00000 n +0003397659 00000 n +0003397816 00000 n +0003510974 00000 n +0003511138 00000 n +0003398687 00000 n +0003396831 00000 n +0003394191 00000 n +0003398321 00000 n +0003398625 00000 n +0017571844 00000 n +0017572033 00000 n +0017572096 00000 n +0017571907 00000 n +0017571970 00000 n 0000000945 00000 f -0003511285 00000 n -0003400851 00000 n -0003511462 00000 n -0003511862 00000 n -0003400680 00000 n -0003398786 00000 n -0003511615 00000 n -0003509796 00000 n -0017572150 00000 n -0017572213 00000 n -0003513540 00000 n -0003513063 00000 n -0003512010 00000 n -0003513175 00000 n -0003513235 00000 n -0003513296 00000 n -0003513357 00000 n -0003513418 00000 n -0003513479 00000 n -0017747331 00000 n -0003513924 00000 n -0003513751 00000 n -0003513651 00000 n -0003513863 00000 n -0003516058 00000 n -0003516210 00000 n -0003516361 00000 n -0003516511 00000 n -0003516663 00000 n -0003516818 00000 n -0003516974 00000 n -0003517129 00000 n -0003517285 00000 n -0003520294 00000 n -0003517756 00000 n -0003515862 00000 n -0003513965 00000 n -0003517452 00000 n -0017572276 00000 n -0003522096 00000 n -0003771772 00000 n -0004028596 00000 n -0004299643 00000 n -0003520454 00000 n -0003520613 00000 n -0003520773 00000 n -0003520933 00000 n -0003521339 00000 n -0003520130 00000 n -0003517841 00000 n -0003521092 00000 n -0004577913 00000 n -0004027913 00000 n -0003521984 00000 n -0003521437 00000 n -0004027792 00000 n -0004577975 00000 n -0004028484 00000 n -0004028049 00000 n -0004577852 00000 n -0004621046 00000 n -0004579915 00000 n -0004591022 00000 n -0004601377 00000 n -0004611369 00000 n -0004680505 00000 n -0004680672 00000 n +0003511297 00000 n +0003400863 00000 n +0003511474 00000 n +0003511874 00000 n +0003400692 00000 n +0003398798 00000 n +0003511627 00000 n +0003509808 00000 n +0017572159 00000 n +0017572222 00000 n +0003513552 00000 n +0003513075 00000 n +0003512022 00000 n +0003513187 00000 n +0003513247 00000 n +0003513308 00000 n +0003513369 00000 n +0003513430 00000 n +0003513491 00000 n +0017747340 00000 n +0003513936 00000 n +0003513763 00000 n +0003513663 00000 n +0003513875 00000 n +0003516070 00000 n +0003516222 00000 n +0003516373 00000 n +0003516523 00000 n +0003516675 00000 n +0003516830 00000 n +0003516986 00000 n +0003517141 00000 n +0003517297 00000 n +0003520306 00000 n +0003517768 00000 n +0003515874 00000 n +0003513977 00000 n +0003517464 00000 n +0017572285 00000 n +0003522108 00000 n +0003771784 00000 n +0004028608 00000 n +0004299655 00000 n +0003520466 00000 n +0003520625 00000 n +0003520785 00000 n +0003520945 00000 n +0003521351 00000 n +0003520142 00000 n +0003517853 00000 n +0003521104 00000 n +0004577925 00000 n +0004027925 00000 n +0003521996 00000 n +0003521449 00000 n +0004027804 00000 n +0004577987 00000 n +0004028496 00000 n +0004028061 00000 n +0004577864 00000 n +0004621058 00000 n +0004579927 00000 n +0004591034 00000 n +0004601389 00000 n +0004611381 00000 n +0004680517 00000 n +0004680684 00000 n 0000000961 00000 f -0004680832 00000 n -0004623736 00000 n -0004621387 00000 n -0004579768 00000 n -0004578111 00000 n -0004621206 00000 n -0017747449 00000 n -0004587377 00000 n -0004597711 00000 n -0004607631 00000 n -0004617323 00000 n -0004680999 00000 n -0004681161 00000 n -0004681321 00000 n -0004681473 00000 n +0004680844 00000 n +0004623748 00000 n +0004621399 00000 n +0004579780 00000 n +0004578123 00000 n +0004621218 00000 n +0017747458 00000 n +0004587389 00000 n +0004597723 00000 n +0004607643 00000 n +0004617335 00000 n +0004681011 00000 n +0004681173 00000 n +0004681333 00000 n +0004681485 00000 n 0000001136 00000 f -0004750640 00000 n -0004684547 00000 n -0004700172 00000 n -0004716408 00000 n -0004733774 00000 n -0004681940 00000 n -0004623556 00000 n -0004621564 00000 n -0004681632 00000 n -0017572402 00000 n -0017572465 00000 n -0004750809 00000 n -0004750965 00000 n -0004751366 00000 n -0004684384 00000 n -0004682075 00000 n -0004751124 00000 n -0004697395 00000 n -0004713631 00000 n -0004730997 00000 n -0004747863 00000 n -0004754727 00000 n -0004754883 00000 n -0004755227 00000 n -0004754587 00000 n -0004751556 00000 n -0004755042 00000 n -0004756816 00000 n -0004756972 00000 n -0004757128 00000 n -0004757284 00000 n -0004757438 00000 n -0004757592 00000 n -0004758112 00000 n -0004756644 00000 n -0004755338 00000 n -0004757744 00000 n -0004757865 00000 n -0004757926 00000 n -0004757988 00000 n -0004758050 00000 n -0004758489 00000 n -0004758311 00000 n -0004758210 00000 n -0004758426 00000 n -0004760491 00000 n -0004760645 00000 n -0004760799 00000 n -0004760952 00000 n -0004761104 00000 n -0004761258 00000 n -0004761415 00000 n -0004761572 00000 n -0004762292 00000 n -0004760291 00000 n -0004758531 00000 n -0004761729 00000 n -0004761915 00000 n -0004761978 00000 n -0004762041 00000 n -0004762104 00000 n -0004762229 00000 n -0017747568 00000 n -0004764672 00000 n -0004764830 00000 n -0004764991 00000 n -0004765152 00000 n -0004767032 00000 n -0004764508 00000 n -0004762378 00000 n -0004765313 00000 n -0004765502 00000 n -0004765566 00000 n -0004765626 00000 n -0004765690 00000 n -0004765754 00000 n -0004765818 00000 n -0004765882 00000 n -0004765946 00000 n -0004766010 00000 n -0004766074 00000 n -0004766138 00000 n -0004766201 00000 n -0004766265 00000 n -0004766329 00000 n -0004766393 00000 n -0004766457 00000 n -0004766521 00000 n -0004766585 00000 n -0004766649 00000 n -0004766713 00000 n -0004766776 00000 n -0004766840 00000 n -0004766904 00000 n -0004766968 00000 n -0004770860 00000 n -0004771020 00000 n -0004771180 00000 n -0004771342 00000 n -0004771501 00000 n -0004778021 00000 n -0004773315 00000 n -0004770687 00000 n -0004767144 00000 n -0004771663 00000 n -0004771849 00000 n -0004771912 00000 n -0004771976 00000 n -0004772040 00000 n -0004772104 00000 n -0004772168 00000 n -0004772232 00000 n -0004772294 00000 n -0004772358 00000 n -0004772422 00000 n -0004772486 00000 n -0004772550 00000 n -0004772614 00000 n -0004772678 00000 n -0004772742 00000 n -0004772806 00000 n -0004772869 00000 n -0004772933 00000 n -0004772997 00000 n -0004773061 00000 n -0004773125 00000 n -0004773189 00000 n -0017578660 00000 n -0004778182 00000 n -0004780579 00000 n -0004777875 00000 n -0004773440 00000 n -0004778344 00000 n -0004778470 00000 n -0004778534 00000 n -0004778598 00000 n -0004778662 00000 n -0004778726 00000 n -0004778790 00000 n -0004778854 00000 n -0004778918 00000 n -0004778981 00000 n -0004779045 00000 n -0004779109 00000 n -0004779173 00000 n -0004779237 00000 n -0004779301 00000 n -0004779365 00000 n -0004779429 00000 n -0004779493 00000 n -0004779557 00000 n -0004779621 00000 n -0004779685 00000 n -0004779749 00000 n -0004779813 00000 n -0004779877 00000 n -0004779940 00000 n -0004780004 00000 n -0004780068 00000 n -0004780132 00000 n -0004780196 00000 n -0004780260 00000 n -0004780387 00000 n -0004780451 00000 n -0004780515 00000 n -0005348314 00000 n -0005348470 00000 n -0005348627 00000 n -0005348784 00000 n -0005348941 00000 n -0005349102 00000 n -0005349259 00000 n -0005349726 00000 n -0005349886 00000 n +0004750652 00000 n +0004684559 00000 n +0004700184 00000 n +0004716420 00000 n +0004733786 00000 n +0004681952 00000 n +0004623568 00000 n +0004621576 00000 n +0004681644 00000 n +0017572411 00000 n +0017572474 00000 n +0004750821 00000 n +0004750977 00000 n +0004751378 00000 n +0004684396 00000 n +0004682087 00000 n +0004751136 00000 n +0004697407 00000 n +0004713643 00000 n +0004731009 00000 n +0004747875 00000 n +0004754739 00000 n +0004754895 00000 n +0004755239 00000 n +0004754599 00000 n +0004751568 00000 n +0004755054 00000 n +0004756828 00000 n +0004756984 00000 n +0004757140 00000 n +0004757296 00000 n +0004757450 00000 n +0004757604 00000 n +0004758124 00000 n +0004756656 00000 n +0004755350 00000 n +0004757756 00000 n +0004757877 00000 n +0004757938 00000 n +0004758000 00000 n +0004758062 00000 n +0004758501 00000 n +0004758323 00000 n +0004758222 00000 n +0004758438 00000 n +0004760505 00000 n +0004760659 00000 n +0004760813 00000 n +0004760966 00000 n +0004761118 00000 n +0004761272 00000 n +0004761429 00000 n +0004761586 00000 n +0004762306 00000 n +0004760305 00000 n +0004758543 00000 n +0004761743 00000 n +0004761929 00000 n +0004761992 00000 n +0004762055 00000 n +0004762118 00000 n +0004762243 00000 n +0017747577 00000 n +0004764686 00000 n +0004764844 00000 n +0004765005 00000 n +0004765166 00000 n +0004767046 00000 n +0004764522 00000 n +0004762392 00000 n +0004765327 00000 n +0004765516 00000 n +0004765580 00000 n +0004765640 00000 n +0004765704 00000 n +0004765768 00000 n +0004765832 00000 n +0004765896 00000 n +0004765960 00000 n +0004766024 00000 n +0004766088 00000 n +0004766152 00000 n +0004766215 00000 n +0004766279 00000 n +0004766343 00000 n +0004766407 00000 n +0004766471 00000 n +0004766535 00000 n +0004766599 00000 n +0004766663 00000 n +0004766727 00000 n +0004766790 00000 n +0004766854 00000 n +0004766918 00000 n +0004766982 00000 n +0004770876 00000 n +0004771037 00000 n +0004771197 00000 n +0004771359 00000 n +0004771519 00000 n +0004778040 00000 n +0004773334 00000 n +0004770703 00000 n +0004767158 00000 n +0004771681 00000 n +0004771867 00000 n +0004771930 00000 n +0004771994 00000 n +0004772058 00000 n +0004772122 00000 n +0004772185 00000 n +0004772249 00000 n +0004772313 00000 n +0004772377 00000 n +0004772441 00000 n +0004772505 00000 n +0004772569 00000 n +0004772633 00000 n +0004772697 00000 n +0004772760 00000 n +0004772824 00000 n +0004772888 00000 n +0004772952 00000 n +0004773016 00000 n +0004773080 00000 n +0004773144 00000 n +0004773208 00000 n +0017578669 00000 n +0004778201 00000 n +0004780598 00000 n +0004777894 00000 n +0004773459 00000 n +0004778363 00000 n +0004778489 00000 n +0004778553 00000 n +0004778617 00000 n +0004778681 00000 n +0004778745 00000 n +0004778809 00000 n +0004778873 00000 n +0004778937 00000 n +0004779000 00000 n +0004779064 00000 n +0004779128 00000 n +0004779192 00000 n +0004779256 00000 n +0004779320 00000 n +0004779384 00000 n +0004779448 00000 n +0004779512 00000 n +0004779576 00000 n +0004779640 00000 n +0004779704 00000 n +0004779768 00000 n +0004779832 00000 n +0004779896 00000 n +0004779959 00000 n +0004780023 00000 n +0004780087 00000 n +0004780151 00000 n +0004780215 00000 n +0004780279 00000 n +0004780406 00000 n +0004780470 00000 n +0004780534 00000 n +0005348333 00000 n +0005348489 00000 n +0005348646 00000 n +0005348803 00000 n +0005348960 00000 n +0005349121 00000 n +0005349278 00000 n +0005349745 00000 n +0005349905 00000 n 0000001177 00000 f -0005349415 00000 n -0004782796 00000 n -0005349572 00000 n -0005354510 00000 n -0005354671 00000 n -0005354832 00000 n -0005350350 00000 n -0004782554 00000 n -0004780704 00000 n -0005350039 00000 n -0005350287 00000 n -0005346414 00000 n -0005354999 00000 n -0005355155 00000 n -0005355311 00000 n -0005509125 00000 n -0005356428 00000 n -0005354328 00000 n -0005350500 00000 n -0005355472 00000 n -0005355661 00000 n -0005355725 00000 n -0005355789 00000 n -0005355853 00000 n -0005355917 00000 n -0005355981 00000 n -0005356044 00000 n -0005356108 00000 n -0005356172 00000 n -0005356236 00000 n -0005356300 00000 n -0005356364 00000 n -0017578723 00000 n -0017578786 00000 n -0017578849 00000 n -0017578912 00000 n -0005509287 00000 n -0005509449 00000 n -0005510088 00000 n -0005510248 00000 n +0005349434 00000 n +0004782815 00000 n +0005349591 00000 n +0005354529 00000 n +0005354690 00000 n +0005354851 00000 n +0005350369 00000 n +0004782573 00000 n +0004780723 00000 n +0005350058 00000 n +0005350306 00000 n +0005346433 00000 n +0005355018 00000 n +0005355174 00000 n +0005355330 00000 n +0005509144 00000 n +0005356447 00000 n +0005354347 00000 n +0005350519 00000 n +0005355491 00000 n +0005355680 00000 n +0005355744 00000 n +0005355808 00000 n +0005355872 00000 n +0005355936 00000 n +0005356000 00000 n +0005356063 00000 n +0005356127 00000 n +0005356191 00000 n +0005356255 00000 n +0005356319 00000 n +0005356383 00000 n +0017578732 00000 n +0017578795 00000 n +0017578858 00000 n +0017578921 00000 n +0005509306 00000 n +0005509468 00000 n +0005510107 00000 n +0005510267 00000 n 0000001394 00000 f -0005509606 00000 n -0005359845 00000 n -0005509766 00000 n -0005509926 00000 n -0005515962 00000 n -0005516124 00000 n -0005511921 00000 n -0005359630 00000 n -0005356553 00000 n -0005510401 00000 n -0005510649 00000 n -0005510711 00000 n -0005510775 00000 n -0005510839 00000 n -0005510902 00000 n -0005510966 00000 n -0005511030 00000 n -0005511094 00000 n -0005511157 00000 n -0005511221 00000 n -0005511285 00000 n -0005511349 00000 n -0005511413 00000 n -0005511477 00000 n -0005511541 00000 n -0005511605 00000 n -0005511669 00000 n -0005511731 00000 n -0005511795 00000 n -0005511859 00000 n -0017747693 00000 n -0005508118 00000 n -0005516285 00000 n -0005516442 00000 n -0005516604 00000 n -0005521995 00000 n -0005522157 00000 n -0005518933 00000 n -0005515789 00000 n -0005512071 00000 n -0005516765 00000 n -0005516891 00000 n -0005516955 00000 n -0005517018 00000 n -0005517082 00000 n -0005517146 00000 n -0005517210 00000 n -0005517274 00000 n -0005517464 00000 n -0005517528 00000 n -0005517592 00000 n -0005517656 00000 n -0005517720 00000 n -0005517784 00000 n -0005517847 00000 n -0005517911 00000 n -0005517975 00000 n -0005518039 00000 n -0005518103 00000 n -0005518167 00000 n -0005518231 00000 n -0005518295 00000 n -0005518359 00000 n -0005518423 00000 n -0005518487 00000 n -0005518551 00000 n -0005518615 00000 n -0005518679 00000 n -0005518742 00000 n -0005518806 00000 n -0005518870 00000 n -0005522318 00000 n -0005522480 00000 n -0005522642 00000 n -0005524891 00000 n -0005521822 00000 n -0005519058 00000 n -0005522795 00000 n -0005522919 00000 n -0005522982 00000 n -0005523045 00000 n -0005523109 00000 n -0005523172 00000 n -0005523298 00000 n -0005523361 00000 n -0005523425 00000 n -0005523489 00000 n -0005523553 00000 n -0005523617 00000 n -0005523680 00000 n -0005523744 00000 n -0005523808 00000 n -0005523872 00000 n -0005523936 00000 n -0005524000 00000 n -0005524064 00000 n -0005524128 00000 n -0005524192 00000 n -0005524255 00000 n -0005524319 00000 n -0005524445 00000 n -0005524508 00000 n -0005524572 00000 n -0005524636 00000 n -0005524699 00000 n -0005524763 00000 n -0005524827 00000 n -0005528910 00000 n -0005529072 00000 n -0005529234 00000 n -0005531626 00000 n -0005528755 00000 n -0005525003 00000 n -0005529392 00000 n -0005529581 00000 n -0005529645 00000 n -0005529709 00000 n -0005529773 00000 n -0005529837 00000 n -0005529901 00000 n -0005529965 00000 n -0005530028 00000 n -0005530092 00000 n -0005530156 00000 n -0005530220 00000 n -0005530284 00000 n -0005530348 00000 n -0005530412 00000 n -0005530475 00000 n -0005530539 00000 n -0005530603 00000 n -0005530667 00000 n -0005530731 00000 n -0005530795 00000 n -0005530859 00000 n -0005530923 00000 n -0005530987 00000 n -0005531051 00000 n -0005531114 00000 n -0005531178 00000 n -0005531242 00000 n -0005531306 00000 n -0005531370 00000 n -0005531434 00000 n -0005531498 00000 n -0005531562 00000 n -0005535694 00000 n -0005535856 00000 n -0005536014 00000 n -0005538145 00000 n -0005535539 00000 n -0005531751 00000 n -0005536176 00000 n -0005536300 00000 n -0005536363 00000 n -0005536427 00000 n -0005536491 00000 n -0005536555 00000 n -0005536619 00000 n -0005536683 00000 n -0005536746 00000 n -0005536810 00000 n -0005536996 00000 n -0005537059 00000 n -0005537123 00000 n -0005537187 00000 n -0005537251 00000 n -0005537315 00000 n -0005537379 00000 n -0005537443 00000 n -0005537506 00000 n -0005537570 00000 n -0005537634 00000 n -0005537698 00000 n -0005537762 00000 n -0005537826 00000 n -0005537890 00000 n -0005537954 00000 n -0005538017 00000 n -0005538081 00000 n -0006422802 00000 n -0006422956 00000 n -0006423117 00000 n -0006423279 00000 n -0006423437 00000 n -0006423599 00000 n -0006424383 00000 n -0006424545 00000 n -0006424706 00000 n -0006424860 00000 n -0006425014 00000 n -0005541069 00000 n -0006423754 00000 n -0006423909 00000 n -0006424067 00000 n -0006424225 00000 n -0007544313 00000 n -0007544470 00000 n -0007544632 00000 n -0006425487 00000 n -0005540791 00000 n -0005538270 00000 n -0006425171 00000 n -0006425423 00000 n -0006421694 00000 n -0006427181 00000 n -0007544790 00000 n -0006548615 00000 n -0007545198 00000 n -0006427002 00000 n -0006425624 00000 n -0007544951 00000 n -0017747818 00000 n -0006547639 00000 n -0007542413 00000 n -0008495676 00000 n +0005509625 00000 n +0005359864 00000 n +0005509785 00000 n +0005509945 00000 n +0005515981 00000 n +0005516143 00000 n +0005511940 00000 n +0005359649 00000 n +0005356572 00000 n +0005510420 00000 n +0005510668 00000 n +0005510730 00000 n +0005510794 00000 n +0005510858 00000 n +0005510921 00000 n +0005510985 00000 n +0005511049 00000 n +0005511113 00000 n +0005511176 00000 n +0005511240 00000 n +0005511304 00000 n +0005511368 00000 n +0005511432 00000 n +0005511496 00000 n +0005511560 00000 n +0005511624 00000 n +0005511688 00000 n +0005511750 00000 n +0005511814 00000 n +0005511878 00000 n +0017747702 00000 n +0005508137 00000 n +0005516304 00000 n +0005516461 00000 n +0005516623 00000 n +0005522014 00000 n +0005522176 00000 n +0005518952 00000 n +0005515808 00000 n +0005512090 00000 n +0005516784 00000 n +0005516910 00000 n +0005516974 00000 n +0005517037 00000 n +0005517101 00000 n +0005517165 00000 n +0005517229 00000 n +0005517293 00000 n +0005517483 00000 n +0005517547 00000 n +0005517611 00000 n +0005517675 00000 n +0005517739 00000 n +0005517803 00000 n +0005517866 00000 n +0005517930 00000 n +0005517994 00000 n +0005518058 00000 n +0005518122 00000 n +0005518186 00000 n +0005518250 00000 n +0005518314 00000 n +0005518378 00000 n +0005518442 00000 n +0005518506 00000 n +0005518570 00000 n +0005518634 00000 n +0005518698 00000 n +0005518761 00000 n +0005518825 00000 n +0005518889 00000 n +0005522337 00000 n +0005522499 00000 n +0005522661 00000 n +0005524910 00000 n +0005521841 00000 n +0005519077 00000 n +0005522814 00000 n +0005522938 00000 n +0005523001 00000 n +0005523064 00000 n +0005523128 00000 n +0005523191 00000 n +0005523317 00000 n +0005523380 00000 n +0005523444 00000 n +0005523508 00000 n +0005523572 00000 n +0005523636 00000 n +0005523699 00000 n +0005523763 00000 n +0005523827 00000 n +0005523891 00000 n +0005523955 00000 n +0005524019 00000 n +0005524083 00000 n +0005524147 00000 n +0005524211 00000 n +0005524274 00000 n +0005524338 00000 n +0005524464 00000 n +0005524527 00000 n +0005524591 00000 n +0005524655 00000 n +0005524718 00000 n +0005524782 00000 n +0005524846 00000 n +0005528929 00000 n +0005529091 00000 n +0005529253 00000 n +0005531645 00000 n +0005528774 00000 n +0005525022 00000 n +0005529411 00000 n +0005529600 00000 n +0005529664 00000 n +0005529728 00000 n +0005529792 00000 n +0005529856 00000 n +0005529920 00000 n +0005529984 00000 n +0005530047 00000 n +0005530111 00000 n +0005530175 00000 n +0005530239 00000 n +0005530303 00000 n +0005530367 00000 n +0005530431 00000 n +0005530494 00000 n +0005530558 00000 n +0005530622 00000 n +0005530686 00000 n +0005530750 00000 n +0005530814 00000 n +0005530878 00000 n +0005530942 00000 n +0005531006 00000 n +0005531070 00000 n +0005531133 00000 n +0005531197 00000 n +0005531261 00000 n +0005531325 00000 n +0005531389 00000 n +0005531453 00000 n +0005531517 00000 n +0005531581 00000 n +0005535713 00000 n +0005535875 00000 n +0005536033 00000 n +0005538164 00000 n +0005535558 00000 n +0005531770 00000 n +0005536195 00000 n +0005536319 00000 n +0005536382 00000 n +0005536446 00000 n +0005536510 00000 n +0005536574 00000 n +0005536638 00000 n +0005536702 00000 n +0005536765 00000 n +0005536829 00000 n +0005537015 00000 n +0005537078 00000 n +0005537142 00000 n +0005537206 00000 n +0005537270 00000 n +0005537334 00000 n +0005537398 00000 n +0005537462 00000 n +0005537525 00000 n +0005537589 00000 n +0005537653 00000 n +0005537717 00000 n +0005537781 00000 n +0005537845 00000 n +0005537909 00000 n +0005537973 00000 n +0005538036 00000 n +0005538100 00000 n +0006422821 00000 n +0006422975 00000 n +0006423136 00000 n +0006423298 00000 n +0006423456 00000 n +0006423618 00000 n +0006424402 00000 n +0006424564 00000 n +0006424725 00000 n +0006424879 00000 n +0006425033 00000 n +0005541088 00000 n +0006423773 00000 n +0006423928 00000 n +0006424086 00000 n +0006424244 00000 n +0007544332 00000 n +0007544489 00000 n +0007544651 00000 n +0006425506 00000 n +0005540810 00000 n +0005538289 00000 n +0006425190 00000 n +0006425442 00000 n +0006421713 00000 n +0006427200 00000 n +0007544809 00000 n +0006548634 00000 n +0007545217 00000 n +0006427021 00000 n +0006425643 00000 n +0007544970 00000 n +0017747827 00000 n +0006547658 00000 n +0007542432 00000 n +0008495695 00000 n 0000001407 00000 f -0008495834 00000 n -0008495996 00000 n -0007547634 00000 n -0008496537 00000 n -0007547464 00000 n -0007545350 00000 n -0008496157 00000 n -0008496283 00000 n -0008496347 00000 n -0008496410 00000 n -0008493899 00000 n -0008499747 00000 n +0008495853 00000 n +0008496015 00000 n +0007547653 00000 n +0008496556 00000 n +0007547483 00000 n +0007545369 00000 n +0008496176 00000 n +0008496302 00000 n +0008496366 00000 n +0008496429 00000 n +0008493918 00000 n +0008499766 00000 n 0000001439 00000 f -0008499904 00000 n -0008500066 00000 n -0008500228 00000 n -0008501972 00000 n -0008499583 00000 n -0008496687 00000 n -0008500390 00000 n -0008500576 00000 n -0008500639 00000 n -0008500703 00000 n -0008500765 00000 n -0008500829 00000 n -0008500893 00000 n -0008500957 00000 n -0008501021 00000 n -0008501085 00000 n -0008501149 00000 n -0008501213 00000 n -0008501338 00000 n -0008501399 00000 n -0008501463 00000 n -0008501527 00000 n -0008501590 00000 n -0008501654 00000 n -0008501718 00000 n -0008501782 00000 n -0008501846 00000 n -0008501908 00000 n -0008506220 00000 n -0008506377 00000 n -0008506535 00000 n +0008499923 00000 n +0008500085 00000 n +0008500247 00000 n +0008501991 00000 n +0008499602 00000 n +0008496706 00000 n +0008500409 00000 n +0008500595 00000 n +0008500658 00000 n +0008500722 00000 n +0008500784 00000 n +0008500848 00000 n +0008500912 00000 n +0008500976 00000 n +0008501040 00000 n +0008501104 00000 n +0008501168 00000 n +0008501232 00000 n +0008501357 00000 n +0008501418 00000 n +0008501482 00000 n +0008501546 00000 n +0008501609 00000 n +0008501673 00000 n +0008501737 00000 n +0008501801 00000 n +0008501865 00000 n +0008501927 00000 n +0008506239 00000 n +0008506396 00000 n +0008506554 00000 n 0000001525 00000 f -0008506696 00000 n -0008512251 00000 n -0008508770 00000 n -0008506056 00000 n -0008502097 00000 n -0008506857 00000 n -0008507046 00000 n -0008507110 00000 n -0008507174 00000 n -0008507238 00000 n -0008507302 00000 n -0008507366 00000 n -0008507430 00000 n -0008507494 00000 n -0008507557 00000 n -0008507621 00000 n -0008507685 00000 n -0008507749 00000 n -0008507813 00000 n -0008507877 00000 n -0008507941 00000 n -0008508005 00000 n -0008508069 00000 n -0008508132 00000 n -0008508196 00000 n -0008508260 00000 n -0008508324 00000 n -0008508388 00000 n -0008508452 00000 n -0008508516 00000 n -0008508580 00000 n -0008508644 00000 n -0008508707 00000 n -0008512413 00000 n -0008512570 00000 n -0008512728 00000 n -0008512885 00000 n -0008513038 00000 n -0008514077 00000 n -0008512069 00000 n -0008508908 00000 n -0008513193 00000 n -0008513317 00000 n -0008513380 00000 n -0008513444 00000 n -0008513508 00000 n -0008513572 00000 n -0008513636 00000 n -0008513700 00000 n -0008513764 00000 n -0008513827 00000 n -0008513889 00000 n -0008514469 00000 n -0008514290 00000 n -0008514189 00000 n -0008514406 00000 n -0008516295 00000 n -0008516452 00000 n -0008516610 00000 n -0008516768 00000 n -0008516925 00000 n -0008517086 00000 n -0008517248 00000 n -0008517411 00000 n -0008519748 00000 n -0010864939 00000 n -0012870254 00000 n -0012870408 00000 n -0008517876 00000 n -0008516095 00000 n -0008514511 00000 n -0008517566 00000 n -0017747943 00000 n -0012870560 00000 n -0012870713 00000 n -0012871186 00000 n -0008519584 00000 n -0008517962 00000 n -0012870871 00000 n -0012903561 00000 n -0012903717 00000 n -0012903879 00000 n -0012904038 00000 n -0012904201 00000 n -0012904358 00000 n +0008506715 00000 n +0008512270 00000 n +0008508789 00000 n +0008506075 00000 n +0008502116 00000 n +0008506876 00000 n +0008507065 00000 n +0008507129 00000 n +0008507193 00000 n +0008507257 00000 n +0008507321 00000 n +0008507385 00000 n +0008507449 00000 n +0008507513 00000 n +0008507576 00000 n +0008507640 00000 n +0008507704 00000 n +0008507768 00000 n +0008507832 00000 n +0008507896 00000 n +0008507960 00000 n +0008508024 00000 n +0008508088 00000 n +0008508151 00000 n +0008508215 00000 n +0008508279 00000 n +0008508343 00000 n +0008508407 00000 n +0008508471 00000 n +0008508535 00000 n +0008508599 00000 n +0008508663 00000 n +0008508726 00000 n +0008512432 00000 n +0008512589 00000 n +0008512747 00000 n +0008512904 00000 n +0008513057 00000 n +0008514096 00000 n +0008512088 00000 n +0008508927 00000 n +0008513212 00000 n +0008513336 00000 n +0008513399 00000 n +0008513463 00000 n +0008513527 00000 n +0008513591 00000 n +0008513655 00000 n +0008513719 00000 n +0008513783 00000 n +0008513846 00000 n +0008513908 00000 n +0008514488 00000 n +0008514309 00000 n +0008514208 00000 n +0008514425 00000 n +0008516314 00000 n +0008516471 00000 n +0008516629 00000 n +0008516787 00000 n +0008516944 00000 n +0008517105 00000 n +0008517267 00000 n +0008517430 00000 n +0008519767 00000 n +0010864958 00000 n +0012870273 00000 n +0012870427 00000 n +0008517895 00000 n +0008516114 00000 n +0008514530 00000 n +0008517585 00000 n +0017747952 00000 n +0012870579 00000 n +0012870732 00000 n +0012871205 00000 n +0008519603 00000 n +0008517981 00000 n +0012870890 00000 n +0012903577 00000 n +0012903733 00000 n +0012903895 00000 n +0012904054 00000 n +0012904217 00000 n +0012904374 00000 n 0000000000 00000 f -0012904519 00000 n -0012873587 00000 n -0012904676 00000 n -0012904832 00000 n -0012947670 00000 n -0012947831 00000 n -0012908076 00000 n -0012905302 00000 n -0012873363 00000 n -0012871338 00000 n -0012904990 00000 n -0012905238 00000 n -0012903115 00000 n -0017579037 00000 n -0017579100 00000 n -0012947992 00000 n -0012948147 00000 n -0012948313 00000 n -0013002324 00000 n -0013002479 00000 n -0013003115 00000 n -0013003271 00000 n -0013002636 00000 n -0012948732 00000 n -0012907888 00000 n -0012905478 00000 n -0012948480 00000 n -0012947147 00000 n -0017579163 00000 n -0017579226 00000 n -0012950812 00000 n -0013002798 00000 n -0013002960 00000 n -0012994009 00000 n -0013003675 00000 n -0012950606 00000 n -0012948908 00000 n -0013003426 00000 n -0017745275 00000 n -0017745609 00000 n -0013003612 00000 n -0012993134 00000 n -0013002007 00000 n -0017540531 00000 n -0017540693 00000 n -0017540851 00000 n -0013005111 00000 n -0017541202 00000 n -0013004941 00000 n -0013003843 00000 n -0017541013 00000 n -0017537947 00000 n -0017542369 00000 n -0017542006 00000 n -0017541352 00000 n -0017542122 00000 n -0017748068 00000 n -0017542748 00000 n -0017542569 00000 n -0017542468 00000 n -0017542685 00000 n -0017544480 00000 n -0017543866 00000 n -0017542790 00000 n -0017543982 00000 n -0017544168 00000 n -0017544231 00000 n -0017545196 00000 n -0017545017 00000 n -0017544566 00000 n -0017545133 00000 n -0017546492 00000 n -0017546066 00000 n -0017545282 00000 n -0017546182 00000 n -0017546858 00000 n -0017546679 00000 n -0017546578 00000 n -0017546795 00000 n -0017548310 00000 n -0017548472 00000 n -0017548634 00000 n -0017548978 00000 n -0017548155 00000 n -0017546900 00000 n -0017548792 00000 n -0017748193 00000 n -0017550484 00000 n -0017550645 00000 n -0017550806 00000 n -0017551090 00000 n -0017550329 00000 n -0017549077 00000 n -0017550964 00000 n -0017554309 00000 n -0017554490 00000 n -0017554887 00000 n -0017555432 00000 n -0017556183 00000 n -0017556583 00000 n -0017556989 00000 n -0017557910 00000 n -0017558379 00000 n -0017558762 00000 n -0017559159 00000 n -0017560069 00000 n -0017560262 00000 n -0017561178 00000 n -0017562544 00000 n -0017553911 00000 n -0017551215 00000 n -0017561555 00000 n -0017554688 00000 n -0017555159 00000 n -0017555682 00000 n -0017555932 00000 n -0017556383 00000 n -0017556786 00000 n -0017557296 00000 n -0017557603 00000 n -0017558144 00000 n -0017558570 00000 n -0017558960 00000 n -0017559462 00000 n -0017559765 00000 n -0017560567 00000 n -0017560872 00000 n -0017561367 00000 n -0017566342 00000 n -0017566529 00000 n -0017566947 00000 n -0017567390 00000 n -0017567828 00000 n -0017568006 00000 n -0017568198 00000 n -0017568568 00000 n -0017569098 00000 n -0017569524 00000 n -0017569950 00000 n -0017570384 00000 n -0017570801 00000 n -0017571219 00000 n -0017575111 00000 n -0017572589 00000 n -0017565998 00000 n -0017562656 00000 n -0017571394 00000 n -0017566738 00000 n -0017567169 00000 n -0017567609 00000 n -0017568384 00000 n -0017568833 00000 n -0017569311 00000 n -0017569737 00000 n -0017570167 00000 n -0017570592 00000 n -0017571010 00000 n -0017575291 00000 n -0017575475 00000 n -0017575916 00000 n -0017576106 00000 n -0017576485 00000 n -0017576864 00000 n -0017577282 00000 n -0017577650 00000 n -0017578017 00000 n -0017578404 00000 n -0017579289 00000 n -0017574821 00000 n -0017572701 00000 n -0017578598 00000 n -0017575695 00000 n -0017576295 00000 n -0017576674 00000 n -0017577073 00000 n -0017577466 00000 n -0017577833 00000 n -0017578210 00000 n -0017579401 00000 n -0017744561 00000 n -0017579425 00000 n -0017744039 00000 n -0017579541 00000 n -0017580018 00000 n -0017580042 00000 n -0017580261 00000 n -0017580287 00000 n -0017580859 00000 n -0017581424 00000 n -0017581902 00000 n -0017582475 00000 n -0017589438 00000 n -0017589663 00000 n -0017597219 00000 n -0017597477 00000 n -0017600470 00000 n -0017600688 00000 n -0017604399 00000 n -0017604630 00000 n -0017610672 00000 n -0017610899 00000 n -0017651232 00000 n -0017651795 00000 n -0017673524 00000 n -0017673951 00000 n -0017699966 00000 n -0017700609 00000 n -0017720007 00000 n -0017720378 00000 n -0017743481 00000 n -0017748309 00000 n -0017748429 00000 n -0017748553 00000 n -0017748643 00000 n -0017748725 00000 n -0017758123 00000 n -0017758335 00000 n -0017758534 00000 n -0017758706 00000 n -0017758883 00000 n -0017759087 00000 n -0017759281 00000 n -0017759521 00000 n -0017759813 00000 n -0017760144 00000 n -0017760454 00000 n -0017760728 00000 n -0017760997 00000 n -0017761288 00000 n -0017761564 00000 n -0017761823 00000 n -0017762082 00000 n -0017762341 00000 n -0017762597 00000 n -0017762854 00000 n -0017763108 00000 n -0017763352 00000 n -0017763585 00000 n -0017763818 00000 n -0017764051 00000 n -0017764284 00000 n -0017764515 00000 n -0017764740 00000 n -0017764965 00000 n -0017765190 00000 n -0017765415 00000 n -0017765640 00000 n -0017765865 00000 n -0017766090 00000 n -0017766315 00000 n -0017766540 00000 n -0017766765 00000 n -0017766990 00000 n -0017767215 00000 n -0017767440 00000 n -0017767665 00000 n -0017767890 00000 n -0017768115 00000 n -0017768340 00000 n -0017768565 00000 n -0017768790 00000 n -0017769015 00000 n -0017769240 00000 n -0017769465 00000 n -0017769690 00000 n -0017769915 00000 n -0017770140 00000 n -0017770365 00000 n -0017770590 00000 n -0017770815 00000 n -0017771040 00000 n -0017771265 00000 n -0017771490 00000 n -0017771715 00000 n -0017771940 00000 n -0017772165 00000 n -0017772390 00000 n -0017772615 00000 n -0017772840 00000 n -0017773065 00000 n -0017773290 00000 n -0017773515 00000 n -0017773740 00000 n -0017773965 00000 n -0017774166 00000 n -0017774337 00000 n -0017774507 00000 n -0017774678 00000 n -0017774848 00000 n -0017775019 00000 n -0017775189 00000 n -0017775362 00000 n -0017775536 00000 n -0017775710 00000 n -0017775887 00000 n -0017776062 00000 n -0017776239 00000 n -0017776414 00000 n -0017776601 00000 n -0017776796 00000 n -0017776996 00000 n -0017777188 00000 n -0017777384 00000 n -0017777587 00000 n -0017777790 00000 n -0017777990 00000 n -0017778185 00000 n -0017778404 00000 n -0017778622 00000 n -0017778841 00000 n -0017779066 00000 n -0017779288 00000 n -0017779507 00000 n -0017779704 00000 n -0017779839 00000 n -0017779961 00000 n -0017780090 00000 n -0017780236 00000 n -0017780369 00000 n -0017780498 00000 n -0017780626 00000 n -0017780754 00000 n -0017780882 00000 n -0017781010 00000 n -0017781138 00000 n -0017781266 00000 n -0017781388 00000 n -0017781503 00000 n -0017781623 00000 n -0017781747 00000 n -0017781872 00000 n -0017781976 00000 n -0017782100 00000 n -0017782222 00000 n -0017782338 00000 n -0017782438 00000 n -0017782478 00000 n -0017782651 00000 n +0012904535 00000 n +0012873603 00000 n +0012904692 00000 n +0012904848 00000 n +0012947686 00000 n +0012947847 00000 n +0012905318 00000 n +0012873379 00000 n +0012871357 00000 n +0012905006 00000 n +0012905254 00000 n +0012903131 00000 n +0017579046 00000 n +0017579109 00000 n +0012908092 00000 n +0012948008 00000 n +0012948163 00000 n +0012948330 00000 n +0013002342 00000 n +0013002497 00000 n +0013003133 00000 n +0013003289 00000 n +0013002654 00000 n +0012948750 00000 n +0012907904 00000 n +0012905494 00000 n +0012948498 00000 n +0012947163 00000 n +0017579172 00000 n +0017579235 00000 n +0012950830 00000 n +0013002816 00000 n +0013002978 00000 n +0012994027 00000 n +0013003693 00000 n +0012950624 00000 n +0012948926 00000 n +0013003444 00000 n +0017745284 00000 n +0017745618 00000 n +0013003630 00000 n +0012993152 00000 n +0013002025 00000 n +0017540540 00000 n +0017540702 00000 n +0017540860 00000 n +0013005120 00000 n +0017541211 00000 n +0013004950 00000 n +0013003861 00000 n +0017541022 00000 n +0017537956 00000 n +0017542378 00000 n +0017542015 00000 n +0017541361 00000 n +0017542131 00000 n +0017748077 00000 n +0017542757 00000 n +0017542578 00000 n +0017542477 00000 n +0017542694 00000 n +0017544489 00000 n +0017543875 00000 n +0017542799 00000 n +0017543991 00000 n +0017544177 00000 n +0017544240 00000 n +0017545205 00000 n +0017545026 00000 n +0017544575 00000 n +0017545142 00000 n +0017546501 00000 n +0017546075 00000 n +0017545291 00000 n +0017546191 00000 n +0017546867 00000 n +0017546688 00000 n +0017546587 00000 n +0017546804 00000 n +0017548319 00000 n +0017548481 00000 n +0017548643 00000 n +0017548987 00000 n +0017548164 00000 n +0017546909 00000 n +0017548801 00000 n +0017748202 00000 n +0017550493 00000 n +0017550654 00000 n +0017550815 00000 n +0017551099 00000 n +0017550338 00000 n +0017549086 00000 n +0017550973 00000 n +0017554318 00000 n +0017554499 00000 n +0017554896 00000 n +0017555441 00000 n +0017556192 00000 n +0017556592 00000 n +0017556998 00000 n +0017557919 00000 n +0017558388 00000 n +0017558771 00000 n +0017559168 00000 n +0017560078 00000 n +0017560271 00000 n +0017561187 00000 n +0017562553 00000 n +0017553920 00000 n +0017551224 00000 n +0017561564 00000 n +0017554697 00000 n +0017555168 00000 n +0017555691 00000 n +0017555941 00000 n +0017556392 00000 n +0017556795 00000 n +0017557305 00000 n +0017557612 00000 n +0017558153 00000 n +0017558579 00000 n +0017558969 00000 n +0017559471 00000 n +0017559774 00000 n +0017560576 00000 n +0017560881 00000 n +0017561376 00000 n +0017566351 00000 n +0017566538 00000 n +0017566956 00000 n +0017567399 00000 n +0017567837 00000 n +0017568015 00000 n +0017568207 00000 n +0017568577 00000 n +0017569107 00000 n +0017569533 00000 n +0017569959 00000 n +0017570393 00000 n +0017570810 00000 n +0017571228 00000 n +0017575120 00000 n +0017572598 00000 n +0017566007 00000 n +0017562665 00000 n +0017571403 00000 n +0017566747 00000 n +0017567178 00000 n +0017567618 00000 n +0017568393 00000 n +0017568842 00000 n +0017569320 00000 n +0017569746 00000 n +0017570176 00000 n +0017570601 00000 n +0017571019 00000 n +0017575300 00000 n +0017575484 00000 n +0017575925 00000 n +0017576115 00000 n +0017576494 00000 n +0017576873 00000 n +0017577291 00000 n +0017577659 00000 n +0017578026 00000 n +0017578413 00000 n +0017579298 00000 n +0017574830 00000 n +0017572710 00000 n +0017578607 00000 n +0017575704 00000 n +0017576304 00000 n +0017576683 00000 n +0017577082 00000 n +0017577475 00000 n +0017577842 00000 n +0017578219 00000 n +0017579410 00000 n +0017744570 00000 n +0017579434 00000 n +0017744048 00000 n +0017579550 00000 n +0017580027 00000 n +0017580051 00000 n +0017580270 00000 n +0017580296 00000 n +0017580868 00000 n +0017581433 00000 n +0017581911 00000 n +0017582484 00000 n +0017589447 00000 n +0017589672 00000 n +0017597228 00000 n +0017597486 00000 n +0017600479 00000 n +0017600697 00000 n +0017604408 00000 n +0017604639 00000 n +0017610681 00000 n +0017610908 00000 n +0017651241 00000 n +0017651804 00000 n +0017673533 00000 n +0017673960 00000 n +0017699975 00000 n +0017700618 00000 n +0017720016 00000 n +0017720387 00000 n +0017743490 00000 n +0017748318 00000 n +0017748438 00000 n +0017748562 00000 n +0017748652 00000 n +0017748734 00000 n +0017758132 00000 n +0017758344 00000 n +0017758543 00000 n +0017758715 00000 n +0017758892 00000 n +0017759096 00000 n +0017759290 00000 n +0017759530 00000 n +0017759822 00000 n +0017760153 00000 n +0017760463 00000 n +0017760737 00000 n +0017761006 00000 n +0017761297 00000 n +0017761573 00000 n +0017761832 00000 n +0017762091 00000 n +0017762350 00000 n +0017762606 00000 n +0017762863 00000 n +0017763117 00000 n +0017763361 00000 n +0017763594 00000 n +0017763827 00000 n +0017764060 00000 n +0017764293 00000 n +0017764524 00000 n +0017764749 00000 n +0017764974 00000 n +0017765199 00000 n +0017765424 00000 n +0017765649 00000 n +0017765874 00000 n +0017766099 00000 n +0017766324 00000 n +0017766549 00000 n +0017766774 00000 n +0017766999 00000 n +0017767224 00000 n +0017767449 00000 n +0017767674 00000 n +0017767899 00000 n +0017768124 00000 n +0017768349 00000 n +0017768574 00000 n +0017768799 00000 n +0017769024 00000 n +0017769249 00000 n +0017769474 00000 n +0017769699 00000 n +0017769924 00000 n +0017770149 00000 n +0017770374 00000 n +0017770599 00000 n +0017770824 00000 n +0017771049 00000 n +0017771274 00000 n +0017771499 00000 n +0017771724 00000 n +0017771949 00000 n +0017772174 00000 n +0017772399 00000 n +0017772624 00000 n +0017772849 00000 n +0017773074 00000 n +0017773299 00000 n +0017773524 00000 n +0017773749 00000 n +0017773974 00000 n +0017774175 00000 n +0017774346 00000 n +0017774516 00000 n +0017774687 00000 n +0017774857 00000 n +0017775028 00000 n +0017775198 00000 n +0017775371 00000 n +0017775545 00000 n +0017775719 00000 n +0017775896 00000 n +0017776071 00000 n +0017776248 00000 n +0017776423 00000 n +0017776610 00000 n +0017776805 00000 n +0017777005 00000 n +0017777197 00000 n +0017777393 00000 n +0017777596 00000 n +0017777799 00000 n +0017777999 00000 n +0017778194 00000 n +0017778413 00000 n +0017778631 00000 n +0017778850 00000 n +0017779075 00000 n +0017779297 00000 n +0017779516 00000 n +0017779713 00000 n +0017779848 00000 n +0017779970 00000 n +0017780099 00000 n +0017780245 00000 n +0017780378 00000 n +0017780507 00000 n +0017780635 00000 n +0017780763 00000 n +0017780891 00000 n +0017781019 00000 n +0017781147 00000 n +0017781275 00000 n +0017781397 00000 n +0017781512 00000 n +0017781632 00000 n +0017781756 00000 n +0017781881 00000 n +0017781985 00000 n +0017782109 00000 n +0017782231 00000 n +0017782347 00000 n +0017782447 00000 n +0017782487 00000 n +0017782660 00000 n trailer << /Size 1864 /Root 1862 0 R /Info 1863 0 R -/ID [<851F25FBA0B296C91B021AB946034516> <851F25FBA0B296C91B021AB946034516>] >> +/ID [<3D8261BF0BE7E00FAA723615BF20985B> <3D8261BF0BE7E00FAA723615BF20985B>] >> startxref -17783070 +17783079 %%EOF