├── .github ├── FUNDING.yml └── workflows │ └── clojure.yml ├── .gitignore ├── LICENSE ├── README.adoc ├── project.clj ├── src └── iss_sim_auto_docking │ ├── calc.clj │ ├── core.clj │ ├── dragon.clj │ └── telemetry.clj └── test └── iss_sim_auto_docking ├── core_test.clj └── telemetry_test.clj /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Danirukun] 4 | ko_fi: danpetrov 5 | -------------------------------------------------------------------------------- /.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: 7 | - 'master' 8 | - '*' 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Install dependencies 18 | run: lein deps 19 | - name: Run tests 20 | run: lein test 21 | - name: Run build 22 | run: lein compile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .hgignore 12 | .hg/ 13 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public 267 | License as published by the Free Software Foundation, either version 2 268 | of the License, or (at your option) any later version, with the GNU 269 | Classpath Exception which is available at 270 | https://www.gnu.org/software/classpath/license.html." 271 | 272 | Simply including a copy of this Agreement, including this Exhibit A 273 | is not sufficient to license the Source Code under Secondary Licenses. 274 | 275 | If it is not possible or desirable to put the notice in a particular 276 | file, then You may include the notice in a location (such as a LICENSE 277 | file in a relevant directory) where a recipient would be likely to 278 | look for such a notice. 279 | 280 | You may add additional accurate notices of copyright ownership. 281 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | :toc: 2 | 3 | = ISS Sim Automatic Docking System 4 | 5 | image:https://github.com/DaniruKun/spacex-iss-docking-sim-autopilot/workflows/Clojure%20CI/badge.svg?branch=master["Github CI", link="https://github.com/DaniruKun/spacex-iss-docking-sim-autopilot"] 6 | 7 | *Video demo:* 8 | 9 | https://www.youtube.com/watch?v=YNYwYZznuMs 10 | 11 | This app performs fully automatic docking of the Crew Dragon in the link:https://iss-sim.spacex.com[SpaceX ISS docking simulator]. 12 | 13 | 14 | Want to read more about it? Check the article on link:https://medium.com/@thedanpetrov/creating-a-spacex-crew-dragon-simulator-autopilot-in-clojure-1ac095d9209b[Medium]. 15 | 16 | == Running 17 | 18 | You can grab a release from link:https://github.com/DaniruKun/spacex-iss-docking-sim-autopilot/releases[Releases], or check the build section. You will need Java installed to run the `.jar` 19 | 20 | You can run the built uberjar with 21 | 22 | java -jar iss-sim-auto-docking-standalone.jar 23 | 24 | 25 | Or you can just double click the `.jar` 26 | 27 | You can also run the project with Leiningen: 28 | 29 | lein run 30 | 31 | == Installation 32 | 33 | === Chrome 34 | 35 | Make sure you have the LATEST Chrome version. 36 | 37 | === Webdriver 38 | 39 | Make sure you have the latest WebDriver (or one compatible with your Chrome version). 40 | 41 | You can install the latest version on macOS with 42 | 43 | brew install chromedriver 44 | 45 | === Java 8 46 | 47 | Make sure you have at least Java 8 installed on your machine, and that it is the active version. 48 | 49 | === Leiningen 50 | 51 | This project uses Leiningen. 52 | 53 | == Build 54 | 55 | You can build an uberjar with 56 | 57 | lein uberjar 58 | 59 | == Quick guide 60 | 61 | === Namespaces 62 | 63 | link:src/iss_sim_auto_docking/core.clj[core] 64 | 65 | Simulator setup and initializations happen, together with main control flow. 66 | 67 | link:src/iss_sim_auto_docking/dragon.clj[dragon] 68 | 69 | Control logic and drivers related to Dragon are implemented. 70 | 71 | link:src/iss_sim_auto_docking/telemetry.clj[telemetry] 72 | 73 | Functions related to fetching telemetry and updating telemetry data. 74 | 75 | link:src/iss_sim_auto_docking/calc.clj[calc] 76 | 77 | Helper calculations. 78 | 79 | == Support 80 | 81 | image::https://www.ko-fi.com/img/githubbutton_sm.svg["Ko-Fi", link="https://ko-fi.com/I3I61NHVO"] 82 | 83 | Or you can just check out my website: link:https://danpetrov.xyz[] 84 | 85 | 86 | == Bugs 87 | 88 | The very final approach speed control is a bit WIP, other than that everything else is fully automatic. 89 | 90 | == Contributing 91 | 92 | Play around with it! Try setting different parameters in the source files. See how it impacts the performance of the autopilot. 93 | 94 | == License 95 | 96 | Copyright © 2020 Daniils Petrovs 97 | 98 | This program and the accompanying materials are made available under the 99 | terms of the Eclipse Public License 2.0 which is available at 100 | http://www.eclipse.org/legal/epl-2.0. 101 | 102 | This Source Code may also be made available under the following Secondary 103 | Licenses when the conditions for such availability set forth in the Eclipse 104 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 105 | the Free Software Foundation, either version 2 of the License, or (at your 106 | option) any later version, with the GNU Classpath Exception which is available 107 | at https://www.gnu.org/software/classpath/license.html. 108 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject iss-sim-auto-docking "0.2.0" 2 | :description "This app does automatic docking in the ISS SpaceX simulator." 3 | :url "https://github.com/DaniruKun/spacex-iss-docking-sim-autopilot" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.10.1"] 7 | [etaoin "0.3.6"] 8 | [org.clojure/math.numeric-tower "0.0.4"]] 9 | :main ^:skip-aot iss-sim-auto-docking.core 10 | :target-path "target/%s" 11 | :jar-name "iss-sim-auto-docking.jar" 12 | :uberjar-name "iss-sim-auto-docking-standalone.jar" 13 | :min-lein-version "2.0.0" 14 | :repl-options {:init-ns iss-sim-auto-docking.core 15 | :init (do 16 | (use 'etaoin.api) 17 | (require '[etaoin.keys :as k]) 18 | (require '[iss-sim-auto-docking.dragon :as dragon]) 19 | (require '[iss-sim-auto-docking.telemetry :as tel]) 20 | (require '[iss-sim-auto-docking.calc :as calc]))} 21 | :profiles {:uberjar {:aot :all} 22 | :dev {:plugins [[lein-cljfmt "0.5.7"]]}}) 23 | -------------------------------------------------------------------------------- /src/iss_sim_auto_docking/calc.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.calc 2 | (:gen-class)) 3 | 4 | (defn get-vx 5 | "Calculate velocity vector x component." 6 | [dt dx] 7 | (float (/ dx dt))) 8 | 9 | (defn get-vy 10 | "Calculate velocity vector y component." 11 | [dt dy] 12 | (float (/ dy dt))) 13 | 14 | (defn get-vz 15 | "Calculate velocity vector z component." 16 | [dt dz] 17 | (float (/ dz dt))) 18 | 19 | -------------------------------------------------------------------------------- /src/iss_sim_auto_docking/core.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.core 2 | (:require 3 | [etaoin.api :refer :all] 4 | [iss-sim-auto-docking.dragon :as dragon] 5 | [iss-sim-auto-docking.telemetry :as tel]) 6 | (:gen-class)) 7 | 8 | (def sim-website-url "https://iss-sim.spacex.com") 9 | 10 | ;; Button selectors 11 | (def begin-button {:id :begin-button}) 12 | (def success {:css "#success > h2"}) 13 | 14 | (defn setup-sim 15 | "Setup the simulator." 16 | [driv] 17 | (println "Setting up the simulator...") 18 | (doto driv 19 | (set-window-size 1200 800) 20 | (go sim-website-url) 21 | (wait-visible begin-button {:timeout 30}) 22 | (click begin-button) 23 | (wait 12)) 24 | (println "Simulator started.")) 25 | 26 | (defn -main 27 | "I don't do a whole lot ... yet." 28 | [& args] 29 | (println "Starting up webdriver...") 30 | (with-chrome {} chr 31 | ;; Setup the simulator 32 | (setup-sim chr) 33 | (println "Started telemetry poller") 34 | (future (tel/poll chr)) 35 | (wait chr 2) 36 | ;; concurrent futures for each control axis 37 | (println "Rotation alignment enabled") 38 | ;; TODO: Replace with core.async channels 39 | (future (dragon/align-roll-rot chr)) 40 | (future (dragon/align-pitch-rot chr)) 41 | (future (dragon/align-yaw-rot chr)) 42 | 43 | (wait chr 10) 44 | (dragon/wait-rotation-stopped) 45 | ;; concurrent futures for each translation axis besides approach axis x 46 | (wait chr 4) 47 | (println "Translation alignment enabled") 48 | (future (dragon/align-z-translation chr)) 49 | (future (dragon/align-y-translation chr)) 50 | (wait chr 5) 51 | 52 | ;; start actual approach to docking port 53 | (dragon/accelerate chr) 54 | (future (dragon/decellerate chr)) 55 | (wait-visible chr success {:timeout 420}) 56 | (println "Docking confirmed") 57 | (wait chr 10)) 58 | (System/exit 0)) 59 | -------------------------------------------------------------------------------- /src/iss_sim_auto_docking/dragon.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.dragon 2 | (:require [etaoin.keys :as k] 3 | [etaoin.api :refer :all] 4 | [clojure.math.numeric-tower :as math] 5 | [iss-sim-auto-docking.telemetry :as tel]) 6 | (:gen-class)) 7 | 8 | (def comma ",") 9 | (def point ".") 10 | 11 | ;; thresholds 12 | (def max-def-rot-rate 0.1) 13 | (def max-approach-rate 2.0) 14 | (def max-final-approach-rate -0.15) 15 | (def max-rotation-error 0.2) 16 | (def min-rot-impulse-interval 400) ;; ms 17 | (def min-transl-impulse-interval 1500) ;; ms 18 | (def max-translation-error 0.21) 19 | (def max-translation-rate 0.04) 20 | (def safezone 15) 21 | (def deadzone 0.3) 22 | 23 | (defn x-within-safe-zone? 24 | "Check if the spaceship is within the safe zone." 25 | [] 26 | (<= (math/abs (@tel/telem :x)) safezone)) 27 | 28 | ;; RCS control functions 29 | 30 | (defn roll 31 | "Roll the Crew Dragon left or right (CCW or CW)." 32 | ([driv dir max-roll-rate] 33 | (if (or 34 | (< (math/abs (@tel/telem :roll-rate)) max-roll-rate) 35 | (and 36 | (pos? (@tel/telem :roll-rate)) (= dir "left")) 37 | (and 38 | (neg? (@tel/telem :roll-rate)) (= dir "right"))) 39 | 40 | (case dir 41 | "left" (fill-active driv comma) 42 | "right" (fill-active driv point))) 43 | (Thread/sleep min-rot-impulse-interval)) 44 | ([driv dir] 45 | (roll driv dir max-def-rot-rate))) 46 | 47 | (defn pitch 48 | "Pitch the Crew Dragon up or down." 49 | ([driv dir max-rot-rate] 50 | (if (or (< (math/abs (@tel/telem :pitch-rate)) max-rot-rate) 51 | (and 52 | (pos? (@tel/telem :pitch-rate)) (= dir "up")) 53 | (and 54 | (neg? (@tel/telem :pitch-rate)) (= dir "down"))) 55 | (case dir 56 | "up" (fill-active driv k/arrow-up) 57 | "down" (fill-active driv k/arrow-down))) 58 | (Thread/sleep min-rot-impulse-interval)) 59 | ([driv dir] 60 | (pitch driv dir max-def-rot-rate))) 61 | 62 | (defn yaw 63 | "Yaw the Crew Dragon port or starboard." 64 | ([driv dir max-rot-rate] 65 | (if (or (< (math/abs (@tel/telem :yaw-rate)) max-rot-rate) 66 | (and 67 | (neg? (@tel/telem :yaw-rate)) (= dir "starboard")) 68 | (and 69 | (pos? (@tel/telem :yaw-rate)) (= dir "port"))) 70 | (case dir 71 | "port" (fill-active driv k/arrow-left) ;; left 72 | "starboard" (fill-active driv k/arrow-right) ;; right 73 | )) 74 | (Thread/sleep min-rot-impulse-interval)) 75 | ([driv dir] 76 | (yaw driv dir max-def-rot-rate))) 77 | 78 | (defn translate 79 | "Translate the Crew Dragon in 6 DoF." 80 | [driv dir] 81 | (case dir 82 | "up" (when (or 83 | (< (math/abs (@tel/telem :vz)) max-translation-rate) 84 | (neg? (@tel/telem :vz))) 85 | (fill-active driv "w")) 86 | "down" (when (or 87 | (< (math/abs (@tel/telem :vz)) max-translation-rate) 88 | (pos? (@tel/telem :vz))) 89 | (fill-active driv "s")) 90 | "left" (when (or 91 | (< (math/abs (@tel/telem :vy)) max-translation-rate) 92 | (pos? (@tel/telem :vy))) 93 | (fill-active driv "a")) 94 | "right" (when (or 95 | (< (math/abs (@tel/telem :vy)) max-translation-rate) 96 | (neg? (@tel/telem :vy))) 97 | (fill-active driv "d")) 98 | "fwd" (when (or 99 | (< (math/abs (@tel/telem :vx)) max-approach-rate) 100 | (pos? (@tel/telem :vx))) 101 | (fill-active driv "e")) 102 | "aft" (when (or 103 | (> (math/abs (@tel/telem :vx)) max-approach-rate) 104 | (and 105 | (neg? (@tel/telem :vx)) 106 | (x-within-safe-zone?) 107 | (< (@tel/telem :vx) -0.2))) 108 | (fill-active driv "q")))) 109 | 110 | ;; Alignment functions 111 | 112 | (defn align-pitch 113 | [driv] 114 | (if (neg? (@tel/telem :pitch)) 115 | (pitch driv "up") 116 | (pitch driv "down"))) 117 | 118 | (defn align-yaw 119 | [driv] 120 | (if (neg? (@tel/telem :yaw)) 121 | (yaw driv "port") 122 | (yaw driv "starboard"))) 123 | 124 | (defn align-roll 125 | [driv] 126 | (if (neg? (@tel/telem :roll)) 127 | (roll driv "left") 128 | (roll driv "right"))) 129 | 130 | (defn align-y 131 | [driv] 132 | (let [y (@tel/telem :y) 133 | vy (@tel/telem :vy)] 134 | (if (pos? y) 135 | (when (or (> (math/abs y) deadzone) 136 | (pos? vy)) 137 | (translate driv "left")) 138 | (when (or (> (math/abs y) deadzone) 139 | (neg? vy)) 140 | (translate driv "right"))))) 141 | 142 | (defn align-z 143 | [driv] 144 | (let [z (@tel/telem :z) 145 | vz (@tel/telem :vz)] 146 | (if (pos? z) 147 | (when (or (> (math/abs z) deadzone) 148 | (pos? vz)) 149 | (translate driv "down")) 150 | (when (or (> (math/abs z) deadzone) 151 | (neg? vz)) 152 | (translate driv "up"))))) 153 | 154 | (defn kill-pitch-rot 155 | "Kill pitch rotation." 156 | [driv] 157 | (when (not (zero? (@tel/telem :pitch-rate))) 158 | (if (pos? (@tel/telem :pitch-rate)) 159 | (pitch driv "up" 0.1) 160 | (pitch driv "down" 0.1)) 161 | (recur driv))) 162 | 163 | (defn kill-yaw-rot 164 | "Kill yaw rotation." 165 | [driv] 166 | (when (not (zero? (@tel/telem :yaw-rate))) 167 | (if (pos? (@tel/telem :yaw-rate)) 168 | (yaw driv "port" 0.1) 169 | (yaw driv "starboard" 0.1)) 170 | (recur driv))) 171 | 172 | (defn kill-roll-rot 173 | "Kill roll rotation." 174 | [driv] 175 | (when (not (zero? (@tel/telem :roll-rate))) 176 | (if (pos? (@tel/telem :roll-rate)) 177 | (roll driv "left" 0.1) 178 | (roll driv "right" 0.1)) 179 | (recur driv))) 180 | 181 | (defn kill-y-translation 182 | "Kill y axis translation velocity." 183 | [driv] 184 | (let [vy (@tel/telem :vy)] 185 | (when (not (zero? vy)) 186 | (if (pos? vy) 187 | (translate driv "left") 188 | (translate driv "right")) 189 | (Thread/sleep min-transl-impulse-interval) 190 | (recur driv)))) 191 | 192 | (defn kill-z-translation 193 | "Kill z axis translation velocity." 194 | [driv] 195 | (let [vz (@tel/telem :vz)] 196 | (when (not (zero? vz)) 197 | (if (pos? vz) 198 | (translate driv "down") 199 | (translate driv "up")) 200 | (Thread/sleep min-transl-impulse-interval) 201 | (recur driv)))) 202 | 203 | ;; Internal functions 204 | 205 | (defn pitch-within-error? [] 206 | (<= (math/abs (@tel/telem :pitch)) max-rotation-error)) 207 | 208 | (defn yaw-within-error? [] 209 | (<= (math/abs (@tel/telem :yaw)) max-rotation-error)) 210 | 211 | (defn roll-within-error? [] 212 | (<= (math/abs (@tel/telem :roll)) max-rotation-error)) 213 | 214 | (defn y-within-error? [] 215 | (<= (math/abs (@tel/telem :y)) max-translation-error)) 216 | 217 | (defn z-within-error? [] 218 | (<= (math/abs (@tel/telem :z)) max-translation-error)) 219 | 220 | (defn wait-rotation-stopped 221 | "Keep polling telemetry until rotation stop has been confirmed." [] 222 | (if (and 223 | (zero? (@tel/telem :roll-rate)) 224 | (zero? (@tel/telem :pitch-rate)) 225 | (zero? (@tel/telem :yaw-rate))) 226 | true 227 | (recur))) 228 | 229 | ;; Public functions 230 | 231 | ; Rotation alignment 232 | 233 | (defn align-roll-rot 234 | "Perform roll alignment." 235 | [driv] 236 | (if (roll-within-error?) 237 | (kill-roll-rot driv) 238 | (align-roll driv)) 239 | (recur driv)) 240 | 241 | (defn align-pitch-rot 242 | "Perform pitch alignment." 243 | [driv] 244 | (if (pitch-within-error?) 245 | (kill-pitch-rot driv) 246 | (align-pitch driv)) 247 | (recur driv)) 248 | 249 | (defn align-yaw-rot 250 | "Perform yaw alignment." 251 | [driv] 252 | (if (yaw-within-error?) 253 | (kill-yaw-rot driv) 254 | (align-yaw driv)) 255 | (recur driv)) 256 | 257 | ; Translation alignment 258 | 259 | (defn align-y-translation 260 | "Keep Dragon aligned on y axis." 261 | [driv] 262 | (if (y-within-error?) 263 | (kill-y-translation driv) 264 | (align-y driv)) 265 | (Thread/sleep min-transl-impulse-interval) 266 | (recur driv)) 267 | 268 | (defn align-z-translation 269 | "Keep Dragon aligned on z axis." 270 | [driv] 271 | (if (z-within-error?) 272 | (kill-z-translation driv) 273 | (align-z driv)) 274 | (Thread/sleep min-transl-impulse-interval) 275 | (recur driv)) 276 | 277 | (defn accelerate 278 | [driv] 279 | (repeat 20 (translate driv "fwd"))) 280 | 281 | (defn decellerate 282 | [driv] 283 | (Thread/sleep 1000) 284 | (if (< (@tel/telem :x) safezone) 285 | (repeat 18 (translate driv "aft")) 286 | (recur driv))) 287 | -------------------------------------------------------------------------------- /src/iss_sim_auto_docking/telemetry.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.telemetry 2 | (:require [iss-sim-auto-docking.calc :as calc] 3 | [clojure.string :as str] 4 | [etaoin.keys :as k] 5 | [etaoin.api :refer :all] 6 | [clojure.math.numeric-tower :as math]) 7 | (:gen-class)) 8 | 9 | (def telem (atom {:x 200 10 | :y 12 11 | :z 30 12 | :t (System/currentTimeMillis)})) 13 | 14 | (def deg (new String "°")) 15 | (def emptystr (new String "")) 16 | 17 | (def poll-interval 150) ;; ms 18 | ;; Internal functions 19 | 20 | (defn parse-delta 21 | [delta] 22 | (-> delta 23 | (.split deg) 24 | first 25 | Float/parseFloat)) 26 | 27 | (defn parse-metric 28 | "Parse a telemetry metric" 29 | [metric metric-type] 30 | (Float/parseFloat (str/replace metric (case metric-type 31 | :roll-rate #" °/s" 32 | :range #" m" 33 | :vel #" m/s") emptystr))) 34 | 35 | ;; Roll 36 | 37 | (defn get-roll-delta 38 | "Get the roll displacement in degrees" 39 | [driv] 40 | (let [roll-delta-q (query driv {:css "#roll > div.error"}) 41 | roll-delta (get-element-text-el driv roll-delta-q)] 42 | (parse-delta roll-delta))) 43 | 44 | (defn get-roll-rate 45 | "Get the roll rate in °/s" 46 | [driv] 47 | (let [roll-rate-q (query driv {:css "#roll > div.rate"}) 48 | roll-rate (get-element-text-el driv roll-rate-q)] 49 | (parse-metric roll-rate :roll-rate))) 50 | 51 | ;; Pitch 52 | 53 | (defn get-pitch-delta 54 | "Get the pitch displacement in degrees" 55 | [driv] 56 | (let [pitch-delta-q (query driv {:css "#pitch > div.error"}) 57 | pitch-delta (get-element-text-el driv pitch-delta-q)] 58 | (parse-delta pitch-delta))) 59 | 60 | (defn get-pitch-rate 61 | "docstring" 62 | [driv] 63 | (let [pitch-rate-q (query driv {:css "#pitch > div.rate"}) 64 | pitch-rate (get-element-text-el driv pitch-rate-q)] 65 | (parse-metric pitch-rate :roll-rate))) 66 | 67 | ;; Yaw 68 | 69 | (defn get-yaw-delta 70 | "" 71 | [driv] 72 | (let [yaw-delta-q (query driv {:css "#yaw > div.error"}) 73 | yaw-delta (get-element-text-el driv yaw-delta-q)] 74 | (parse-delta yaw-delta))) 75 | 76 | (defn get-yaw-rate 77 | "" 78 | [driv] 79 | (let [yaw-rate-q (query driv {:css "#yaw > div.rate"}) 80 | yaw-rate (get-element-text-el driv yaw-rate-q)] 81 | (parse-delta yaw-rate))) 82 | 83 | ;; Distance 84 | 85 | (defn get-range 86 | "Get the Euclidian distance to the ISS in meters." 87 | [driv] 88 | (let [range (->> {:css "#range > div.rate"} 89 | (query driv) 90 | (get-element-text-el driv))] 91 | (parse-metric range :range))) 92 | 93 | (defn get-dist 94 | "Get the distance to ISS on a given axis." 95 | [driv axis] 96 | (let [sel (str "#" axis "-range > div") 97 | dist (->> {:css sel} 98 | (query driv) 99 | (get-element-text-el driv))] 100 | (parse-metric dist :range))) 101 | 102 | (defn get-approach-rate 103 | "Get the approach rate relative to ISS." 104 | [driv] 105 | (let [v (->> {:css "#rate > div.rate"} 106 | (query driv) 107 | (get-element-text-el driv))] 108 | (-> v 109 | (parse-metric :vel) 110 | (math/abs)))) 111 | 112 | (defn poll 113 | "Poll telemetry and update the state." 114 | [driv] 115 | (if true ;; TODO add proper termination of telemetry poller by checking if driver is active 116 | (do 117 | (Thread/sleep poll-interval) 118 | (let [t (System/currentTimeMillis) 119 | dt (* (+ poll-interval (- t (@telem :t))) 0.001) 120 | x (get-dist driv "x") 121 | y (get-dist driv "y") 122 | z (get-dist driv "z") 123 | 124 | dx (- x (@telem :x)) 125 | dy (- y (@telem :y)) 126 | dz (- z (@telem :z)) 127 | 128 | vx (calc/get-vx dt dx) 129 | vy (calc/get-vy dt dy) 130 | vz (calc/get-vz dt dz)] 131 | 132 | (swap! telem assoc 133 | :roll (get-roll-delta driv) 134 | :pitch (get-pitch-delta driv) 135 | :yaw (get-yaw-delta driv) 136 | 137 | :roll-rate (get-roll-rate driv) 138 | :pitch-rate (get-pitch-rate driv) 139 | :yaw-rate (get-yaw-rate driv) 140 | 141 | :range (get-range driv) 142 | 143 | :x x 144 | :y y 145 | :z z 146 | 147 | :vx vx 148 | :vy vy 149 | :vz vz 150 | 151 | :t (System/currentTimeMillis))) 152 | 153 | (recur driv)))) 154 | -------------------------------------------------------------------------------- /test/iss_sim_auto_docking/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.core-test 2 | (:require [clojure.test :refer :all] 3 | [iss-sim-auto-docking.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 1 1)))) 8 | -------------------------------------------------------------------------------- /test/iss_sim_auto_docking/telemetry_test.clj: -------------------------------------------------------------------------------- 1 | (ns iss-sim-auto-docking.telemetry_test 2 | (:require [clojure.test :refer :all] 3 | [iss-sim-auto-docking.telemetry :refer :all])) 4 | 5 | (deftest parse-metric-test 6 | "Test metric parser with different inputs" 7 | (testing "Test rate parser" 8 | (is (= (float 1.3) (parse-metric "1.3 °/s" :roll-rate)))) 9 | (testing "Test range parser" 10 | (is (= (float -10.2) (parse-metric "-10.2 m" :range)))) 11 | (testing "Test velocity parser" 12 | (is (= (float 1.4) (parse-metric "1.4 m/s" :vel))))) --------------------------------------------------------------------------------