├── .gitignore ├── .editorconfig ├── README.md ├── source ├── todolist.rst ├── functions.png ├── message1-decoded.txt ├── message1.png ├── message2.png ├── message3.png ├── message4.png ├── message5.png ├── message6.png ├── message7.png ├── message8.png ├── message5-plus.png ├── operands_usage.png ├── message2-encoding1.png ├── radio-transmission-2d.png ├── radio-transmission-2d-rust.png ├── radio-transmission-encoding1.png ├── radio-transmission-encoding2.png ├── radio-transmission-encoding3.png ├── radio-transmission-recording.wav ├── radio-transmission-spectrogram.png ├── message2-decoded.txt ├── message7-decoded.txt ├── message3-decoded.txt ├── message4-decoded.txt ├── message1.rst ├── message6-decoded.txt ├── message5-decoded.txt ├── message8-decoded.txt ├── index.rst ├── message4.rst ├── personal-appeal.rst ├── message3.rst ├── message8.rst ├── message6.rst ├── message7.rst ├── message2.rst ├── decode-wav.rs ├── message5.rst ├── radio-transmission-recording.rst ├── conf.py ├── annotate.py ├── annotate.hs ├── message4-annotated.svg ├── message3-annotated.svg └── message3-annotated-full.svg ├── requirements.txt ├── readthedocs.yml ├── Makefile ├── make.bat ├── LICENSE ├── add-new-message.j2 └── add-new-message.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode 3 | source/condensed-version.rst 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | See rendered version at https://message-from-space.readthedocs.io -------------------------------------------------------------------------------- /source/todolist.rst: -------------------------------------------------------------------------------- 1 | Unfinished pages 2 | ================ 3 | 4 | .. todolist:: 5 | -------------------------------------------------------------------------------- /source/functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/functions.png -------------------------------------------------------------------------------- /source/message1-decoded.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | ... 11 | -------------------------------------------------------------------------------- /source/message1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message1.png -------------------------------------------------------------------------------- /source/message2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message2.png -------------------------------------------------------------------------------- /source/message3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message3.png -------------------------------------------------------------------------------- /source/message4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message4.png -------------------------------------------------------------------------------- /source/message5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message5.png -------------------------------------------------------------------------------- /source/message6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message6.png -------------------------------------------------------------------------------- /source/message7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message7.png -------------------------------------------------------------------------------- /source/message8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message8.png -------------------------------------------------------------------------------- /source/message5-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message5-plus.png -------------------------------------------------------------------------------- /source/operands_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/operands_usage.png -------------------------------------------------------------------------------- /source/message2-encoding1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/message2-encoding1.png -------------------------------------------------------------------------------- /source/radio-transmission-2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-2d.png -------------------------------------------------------------------------------- /source/radio-transmission-2d-rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-2d-rust.png -------------------------------------------------------------------------------- /source/radio-transmission-encoding1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-encoding1.png -------------------------------------------------------------------------------- /source/radio-transmission-encoding2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-encoding2.png -------------------------------------------------------------------------------- /source/radio-transmission-encoding3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-encoding3.png -------------------------------------------------------------------------------- /source/radio-transmission-recording.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-recording.wav -------------------------------------------------------------------------------- /source/radio-transmission-spectrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/message-from-space/master/source/radio-transmission-spectrogram.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow==7.2.0 2 | Sphinx==3.1.2 3 | sphinx_rtd_theme==0.5.0 4 | sphinxcontrib-images==0.9.2 5 | sphinxcontrib-svg2pdfconverter==1.1.0 6 | -------------------------------------------------------------------------------- /source/message2-decoded.txt: -------------------------------------------------------------------------------- 1 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2 | ... 3 | 506 507 508 509 510 511 512 513 514 4 | ... 5 | 65535 65536 65537 6 | ... 7 | -------------------------------------------------------------------------------- /source/message7-decoded.txt: -------------------------------------------------------------------------------- 1 | add 2 | ap ap add 1 2 = 3 3 | ap ap add 2 1 = 3 4 | ap ap add 0 1 = 1 5 | ap ap add 2 3 = 5 6 | ap ap add 3 5 = 8 7 | ... 8 | -------------------------------------------------------------------------------- /source/message3-decoded.txt: -------------------------------------------------------------------------------- 1 | 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 2 | ... 3 | -510 -511 -512 -513 -514 4 | ... 5 | -65535 -65536 -65537 6 | ... 7 | -------------------------------------------------------------------------------- /source/message4-decoded.txt: -------------------------------------------------------------------------------- 1 | = 2 | 0 = 0 3 | 1 = 1 4 | 2 = 2 5 | 3 = 3 6 | ... 7 | 10 = 10 8 | 11 = 11 9 | ... 10 | -1 = -1 11 | -2 = -2 12 | ... 13 | -------------------------------------------------------------------------------- /source/message1.rst: -------------------------------------------------------------------------------- 1 | #1. Numbers 2 | =========== 3 | 4 | THIS FILE IS USED ONLY FOR THE AUTOMATED GENERATION OF CONDENSED VERSION. 5 | 6 | ACTUAL CONTENTS AT radio-transmission-recording.rst 7 | -------------------------------------------------------------------------------- /source/message6-decoded.txt: -------------------------------------------------------------------------------- 1 | dec 2 | ap dec 1 = 0 3 | ap dec 2 = 1 4 | ap dec 3 = 2 5 | ap dec 4 = 3 6 | ... 7 | ap dec 1024 = 1023 8 | ... 9 | ap dec 0 = -1 10 | ap dec -1 = -2 11 | ap dec -2 = -3 12 | ... 13 | -------------------------------------------------------------------------------- /source/message5-decoded.txt: -------------------------------------------------------------------------------- 1 | inc 2 | ap inc 0 = 1 3 | ap inc 1 = 2 4 | ap inc 2 = 3 5 | ap inc 3 = 4 6 | ... 7 | ap inc 300 = 301 8 | ap inc 301 = 302 9 | ... 10 | ap inc -1 = 0 11 | ap inc -2 = -1 12 | ap inc -3 = -2 13 | ... 14 | -------------------------------------------------------------------------------- /source/message8-decoded.txt: -------------------------------------------------------------------------------- 1 | x0 x1 x2 x3 x4 2 | ... 3 | ap ap add 0 x0 = x0 4 | ap ap add 0 x1 = x1 5 | ap ap add 0 x2 = x2 6 | ... 7 | ap ap add x0 0 = x0 8 | ap ap add x1 0 = x1 9 | ap ap add x2 0 = x2 10 | ... 11 | ap ap add x0 x1 = ap ap add x1 x0 12 | ... 13 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | sphinx: 7 | configuration: source/conf.py 8 | 9 | python: 10 | version: 3.8 11 | install: 12 | - requirements: requirements.txt 13 | 14 | formats: 15 | - pdf 16 | - htmlzip 17 | -------------------------------------------------------------------------------- /source/index.rst: -------------------------------------------------------------------------------- 1 | Message From Space 2 | ================== 3 | 4 | This documentation contains artifacts from a joint deciphering effort of a mysterious radio transmission from outer space. 5 | 6 | To read the whole story, go to :doc:`personal-appeal`. 7 | 8 | To skip the investigation part and go straight to the results, go to :doc:`condensed-version`. 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | personal-appeal 14 | condensed-version 15 | radio-transmission-recording 16 | message2 17 | message3 18 | message4 19 | message5 20 | message6 21 | message7 22 | message8 23 | todolist 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pegovka Observatory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /add-new-message.j2: -------------------------------------------------------------------------------- 1 | #{{ message_number }}. ??? 2 | ======== 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | 9 | Image 10 | ----- 11 | 12 | This image was produced from the {{ message_number_words }} radio transmission using :doc:`previously contributed code `. 13 | 14 | .. image:: message{{ message_number }}.png 15 | :width: {{ image_width }}px 16 | 17 | This partly annotated version of the image was made using :ref:`code from message #3 `. 18 | 19 | .. image:: message{{ message_number }}-annotated.svg 20 | :width: {{ image_width }}px 21 | 22 | 23 | Interpretation 24 | -------------- 25 | 26 | .. todo:: 27 | 28 | Add an interpretation for the {{ message_number_words }} message. 29 | 30 | 31 | Decoded 32 | ------- 33 | 34 | .. literalinclude:: message{{ message_number }}-decoded.txt 35 | 36 | 37 | Code 38 | ---- 39 | 40 | .. todo:: 41 | 42 | Revise the :ref:`Haskell code ` to support new glyphs from the {{ message_number_words }} message. 43 | 44 | 45 | Once again, I encourage you to join our `chat server`_ to combine efforts and crack this message. 46 | 47 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message{{ message_number }}.rst 48 | .. _chat server: https://discord.gg/xvMJbas 49 | -------------------------------------------------------------------------------- /source/message4.rst: -------------------------------------------------------------------------------- 1 | #4. Equality 2 | ============ 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the fourth radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message4.png 18 | :width: 100px 19 | 20 | This partly annotated version of the image was made using :ref:`code from message #2 `. 21 | 22 | .. image:: message4-annotated.svg 23 | :width: 100px 24 | 25 | 26 | Interpretation 27 | -------------- 28 | 29 | The new glyph is probably an equality sign, but there is not enough information be sure. 30 | Can be a less-than sign, any operation that preserves its operand, etc. 31 | 32 | 33 | Decoded 34 | ------- 35 | 36 | .. literalinclude:: message4-decoded.txt 37 | 38 | 39 | Code 40 | ---- 41 | 42 | Revised version of the Haskell code that supports the ``=`` glyph is published on the :ref:`message #3 page `. 43 | 44 | Contributed by Discord user @pink_snow. 45 | 46 | Example output: 47 | 48 | .. image:: message4-annotated-full.svg 49 | :width: 100px 50 | 51 | 52 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message4.rst 53 | .. _Discord chat: https://discord.gg/xvMJbas 54 | -------------------------------------------------------------------------------- /source/personal-appeal.rst: -------------------------------------------------------------------------------- 1 | A Personal Appeal to Scientists and Engineers From All Over the World 2 | ===================================================================== 3 | 4 | Hello. 5 | 6 | My name is Ivan Zaitsev. I'm a staff astronomer at Pegovka observatory in the Urals region of Russia. 7 | 8 | Several days ago we have been monitoring radio signals from a small region of sky around `HD 190360`_ for our exoplanet research purposes. We have registered a peculiar radio transmission which cannot be attributed to any natural source in this area. 9 | 10 | We tried to analyze and decode this message. But we don't have any trained deciphering specialists here on site, and we don't have appropriate software to analyze this message. We have made very little progress so far. 11 | 12 | I believe that decoding this message can lead to a major breakthrough in our understanding of the Universe. I believe that science should be a joint effort. Together we can crack this problem much faster that any single research group. 13 | 14 | That's why I decided to publish a :download:`recording of this message ` and create a :doc:`special documentation page ` to collaborate on the explanation. 15 | 16 | If you have any idea at all on how to decode and explain the message, please send a Pull Request to the appropriate page! We cannot move forward without this! 17 | 18 | Sincerely, 19 | Ivan Zaitsev 20 | 21 | .. _HD 190360: http://simbad.u-strasbg.fr/simbad/sim-id?Ident=HD+190360 22 | -------------------------------------------------------------------------------- /source/message3.rst: -------------------------------------------------------------------------------- 1 | #3. Negative numbers 2 | ==================== 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the third radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message3.png 18 | 19 | This partly annotated version of the image was made using :ref:`code from message #2 `. 20 | 21 | .. image:: message3-annotated.svg 22 | 23 | 24 | Interpretation 25 | -------------- 26 | 27 | Contributed by Discord user @pink_snow. 28 | 29 | Looks like the bottom left additional pixel is used to indicate negative numbers. 30 | 31 | 32 | Decoded 33 | ------- 34 | 35 | .. literalinclude:: message3-decoded.txt 36 | 37 | 38 | .. _message3-code: 39 | 40 | Code 41 | ---- 42 | 43 | Revised version of the Python code that supports negative numbers is published on the :ref:`message #2 page `. 44 | 45 | Contributed by Discord user @pink_snow. 46 | 47 | Example output: 48 | 49 | .. image:: message3-annotated-full.svg 50 | 51 | @pink_snow also provided a Haskell version of the same code. 52 | 53 | .. literalinclude:: annotate.hs 54 | :language: haskell 55 | 56 | 57 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message3.rst 58 | .. _Discord chat: https://discord.gg/xvMJbas 59 | -------------------------------------------------------------------------------- /source/message8.rst: -------------------------------------------------------------------------------- 1 | #8. Variables 2 | ============= 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the eighth radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message8.png 18 | :width: 268px 19 | 20 | This partly annotated version of the image was made using :ref:`code from message #3 `. 21 | 22 | .. image:: message8-annotated.svg 23 | :width: 268px 24 | 25 | 26 | Interpretation 27 | -------------- 28 | 29 | Contributed by Discord user @FiddlesticksMcGee. 30 | 31 | Appears to designate a syntax for declaring variables. 32 | 33 | Variables have a border of high values framing a negative of numbers as previously encoded in :doc:`radio-transmission-recording`. 34 | 35 | 36 | Decoded 37 | ------- 38 | 39 | .. literalinclude:: message8-decoded.txt 40 | 41 | 42 | Code 43 | ---- 44 | 45 | Revised version of the Haskell code that supports the variable glyphs is published on the :ref:`message #3 page `. 46 | 47 | Contributed by Discord users @pink_snow and @fryguybob. 48 | 49 | Example output: 50 | 51 | .. image:: message8-annotated-full.svg 52 | :width: 268px 53 | 54 | 55 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message8.rst 56 | .. _Discord chat: https://discord.gg/xvMJbas 57 | -------------------------------------------------------------------------------- /source/message6.rst: -------------------------------------------------------------------------------- 1 | #6. Predecessor 2 | =============== 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the sixth radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message6.png 18 | :width: 156px 19 | 20 | This partly annotated version of the image was made using :ref:`code from message #3 `. 21 | 22 | .. image:: message6-annotated.svg 23 | :width: 156px 24 | 25 | 26 | Interpretation 27 | -------------- 28 | 29 | Contributed by Discord user @elventian. 30 | 31 | Just like in :doc:`message5`, there are two unknown symbols in this message, together they represent a decrement operation. 32 | 33 | The three-pixel symbol is identical to increment operation, and the other complicated one looks similar to increment, 34 | but rejects our theory about internal structure of the symbol, so it's just an arbitrary patterns. 35 | 36 | 37 | Decoded 38 | ------- 39 | 40 | .. literalinclude:: message6-decoded.txt 41 | 42 | 43 | Code 44 | ---- 45 | 46 | Revised version of the Haskell code that supports the ``dec`` glyph is published on the :ref:`message #3 page `. 47 | 48 | Contributed by Discord users @pink_snow and @fryguybob. 49 | 50 | Example output: 51 | 52 | .. image:: message6-annotated-full.svg 53 | :width: 156px 54 | 55 | 56 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message6.rst 57 | .. _Discord chat: https://discord.gg/xvMJbas 58 | -------------------------------------------------------------------------------- /source/message7.rst: -------------------------------------------------------------------------------- 1 | #7. Sum 2 | ======= 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the seventh radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message7.png 18 | :width: 176px 19 | 20 | This partly annotated version of the image was made using :ref:`code from message #3 `. 21 | 22 | .. image:: message7-annotated.svg 23 | :width: 176px 24 | 25 | 26 | Interpretation 27 | -------------- 28 | 29 | Contributed by Discord user @elventian. 30 | 31 | This image shows all known operators and functions: 32 | 33 | .. image:: functions.png 34 | :width: 500px 35 | 36 | Count of operand symbols before function symbol defines how many operands the function expects. 37 | if this is correct, we need to do the following to calculate sum of three numbers: 38 | 39 | .. image:: operands_usage.png 40 | :width: 500px 41 | 42 | 43 | Decoded 44 | ------- 45 | 46 | .. literalinclude:: message7-decoded.txt 47 | 48 | 49 | Code 50 | ---- 51 | 52 | Revised version of the Haskell code that supports the ``add`` glyph is published on the :ref:`message #3 page `. 53 | 54 | Contributed by Discord users @pink_snow and @fryguybob. 55 | 56 | Example output: 57 | 58 | .. image:: message7-annotated-full.svg 59 | :width: 176px 60 | 61 | 62 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message7.rst 63 | .. _Discord chat: https://discord.gg/xvMJbas 64 | -------------------------------------------------------------------------------- /add-new-message.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env nix-shell 2 | #! nix-shell -i python -p "python38.withPackages(ps: with ps; [ pillow inflect jinja2 ])" 3 | 4 | import inflect 5 | import os 6 | import subprocess 7 | import sys 8 | from jinja2 import Template 9 | from PIL import Image 10 | 11 | 12 | if __name__ == '__main__': 13 | if len(sys.argv) < 2: 14 | print('please provide new message number') 15 | exit(1) 16 | 17 | message_number = int(sys.argv[1]) 18 | os.chdir(os.path.join(os.path.dirname(__file__), 'source')) 19 | 20 | if not os.path.isfile('message%d.png' % message_number): 21 | print('image file not found') 22 | exit(1) 23 | 24 | # render annotated SVG and textual representation 25 | subprocess.call([ 26 | './annotate.hs', 27 | 'message%d.png' % message_number, 28 | 'message%d-annotated.svg' % message_number, 29 | 'message%d-decoded.txt' % message_number, 30 | ]) 31 | 32 | # render the main documentation page 33 | with open('../add-new-message.j2') as t: 34 | template = Template(t.read()) 35 | 36 | inflector = inflect.engine() 37 | img = Image.open('message%d.png' % message_number) 38 | 39 | with open('message%d.rst' % message_number, 'w') as m: 40 | m.write(template.render( 41 | message_number=message_number, 42 | message_number_words=inflector.number_to_words(inflector.ordinal(message_number)), 43 | image_width=img.size[0] 44 | )) 45 | 46 | # add new page to TOC 47 | with open('index.rst') as index: 48 | index_contents = index.readlines() 49 | 50 | with open('index.rst', 'w') as index: 51 | for line in index_contents: 52 | if 'todolist' in line: 53 | line = (' message%d\n' % message_number) + line 54 | index.write(line) 55 | -------------------------------------------------------------------------------- /source/message2.rst: -------------------------------------------------------------------------------- 1 | #2. Numbers (cont.) 2 | =================== 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the second radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message2.png 18 | 19 | 20 | Interpretation 21 | -------------- 22 | 23 | Contributed by Discord user @elventian. 24 | 25 | We have enough data to conclude that we've found the way of encoding natural numbers by raster monochrome pictogram framed at the top and left. 26 | There is a square semantic region with side N inside the pictogram. Each pixel of the region corresponds to one bit in the binary notation of the number. 27 | Let x and y be column and row numbers in the range [0 ... N), then the place value for the cell (x, y) is determined by the following formula: 28 | 29 | .. math:: 30 | place\_value(x,y) = 2^{y * N + x} 31 | 32 | Place values for N up to 5: 33 | 34 | .. image:: message2-encoding1.png 35 | 36 | 37 | Decoded 38 | ------- 39 | 40 | Decoded by Discord users @gltronred and @frictionless. 41 | 42 | .. literalinclude:: message2-decoded.txt 43 | 44 | 45 | .. _message2-code: 46 | 47 | Code 48 | ---- 49 | 50 | This Python code decodes and annotates numbers on a provided picture. 51 | 52 | Contributed by Discord user @pink_snow. 53 | 54 | Example output: 55 | 56 | .. image:: message2-annotated-full.svg 57 | 58 | .. literalinclude:: annotate.py 59 | :language: python 60 | 61 | 62 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message2.rst 63 | .. _Discord chat: https://discord.gg/xvMJbas 64 | -------------------------------------------------------------------------------- /source/decode-wav.rs: -------------------------------------------------------------------------------- 1 | // [dependencies] 2 | // png = "0.16" 3 | // hound = "3.4" 4 | 5 | fn main() { 6 | let r = hound::WavReader::open("radio-transmission-recording.wav").unwrap(); 7 | let spec = r.spec(); 8 | let samples: Vec = r.into_samples().map(Result::unwrap).collect(); 9 | 10 | let freq = 600; 11 | let step = 2.0 * std::f32::consts::PI * freq as f32 / spec.sample_rate as f32; 12 | let xys: Vec<(f32, f32)> = samples.iter().copied().enumerate().map(|(i, s)| { 13 | let s = s as f32; 14 | let a = i as f32 * step; 15 | (a.cos() * s, a.sin() * s) 16 | }).collect(); 17 | 18 | let mut axyz = vec![(0.0, 0.0)]; 19 | for (x, y) in xys { 20 | let last = *axyz.last().unwrap(); 21 | axyz.push((last.0 + x, last.1 + y)); 22 | } 23 | 24 | let mut ds: Vec = axyz.iter().zip(axyz.iter().skip(1000)).map(|(xy1, xy2)| { 25 | let dx = xy1.0 - xy2.0; 26 | let dy = xy1.1 - xy2.1; 27 | dx * dx + dy * dy 28 | }).collect(); 29 | let max = *ds.iter().max_by(|x, y| x.partial_cmp(y).unwrap()).unwrap(); 30 | ds.iter_mut().for_each(|x| *x /= max); 31 | 32 | let width = 100usize; 33 | let height = 195usize; 34 | 35 | let w = std::fs::File::create("res.png").unwrap(); 36 | let w = std::io::BufWriter::new(w); 37 | let mut encoder = png::Encoder::new(w, width as u32, height as u32); 38 | encoder.set_color(png::ColorType::Grayscale); 39 | encoder.set_depth(png::BitDepth::Eight); 40 | let mut w = encoder.write_header().unwrap(); 41 | 42 | let mut data = vec![0u8; width * height]; 43 | for (i, cell) in data.iter_mut().enumerate() { 44 | let x = i % width; 45 | let y = i / width / 4; 46 | *cell = (ds.get((x + y * width) * 529 + 132400).copied().unwrap_or(0.0) * 255.0) as u8; 47 | } 48 | w.write_image_data(&data).unwrap(); 49 | } 50 | -------------------------------------------------------------------------------- /source/message5.rst: -------------------------------------------------------------------------------- 1 | #5. Successor 2 | ============= 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 9 | Thanks to everyone who helped! 10 | 11 | 12 | Image 13 | ----- 14 | 15 | This image was produced from the fifth radio transmission using :doc:`previously contributed code `. 16 | 17 | .. image:: message5.png 18 | :width: 148px 19 | 20 | This partly annotated version of the image was made using :ref:`code from message #3 `. 21 | 22 | .. image:: message5-annotated.svg 23 | :width: 148px 24 | 25 | 26 | Interpretation 27 | -------------- 28 | 29 | There are two new symbols in this message, which are used inseparably from each other. 30 | It seems that a combination of them represents an increment operation. 31 | 32 | The three-pixel symbol could be the application operator, and the other complicated one is the successor function. (by @nore) 33 | 34 | The inner part of the complicated symbol is number 1. (by @gltronred) 35 | As a consequence of this observation, this symbol could contain any number: (image by @elventian) 36 | 37 | .. image:: message5-plus.png 38 | :width: 200px 39 | 40 | The numerical value of new symbols are 0 and 417. 41 | 42 | 43 | Decoded 44 | ------- 45 | 46 | .. literalinclude:: message5-decoded.txt 47 | 48 | 49 | Code 50 | ---- 51 | 52 | Revised version of the Haskell code that supports the ``ap`` and ``inc`` glyphs is published on the :ref:`message #3 page `. 53 | 54 | Contributed by Discord users @pink_snow and @fryguybob. 55 | 56 | Example output: 57 | 58 | .. image:: message5-annotated-full.svg 59 | :width: 148px 60 | 61 | 62 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/message4.rst 63 | .. _Discord chat: https://discord.gg/xvMJbas 64 | -------------------------------------------------------------------------------- /source/radio-transmission-recording.rst: -------------------------------------------------------------------------------- 1 | #1. Numbers 2 | =========== 3 | 4 | .. note:: 5 | 6 | If you have any ideas or enhancements for this page, please `edit it on GitHub`_! 7 | 8 | :download:`Download ` radio transmission recording. 9 | It was originally received at ~5 GHz and scaled down to ~500 Hz to make signal audible for humans. 10 | 11 | Following documentation is a cooperative result combined from our `Discord chat`_ and numerous pull requests. 12 | Thanks to everyone who helped! 13 | 14 | 15 | Spectrogram 16 | ----------- 17 | 18 | Spectrogram of the recording, rendered with a `notebook`_ by Discord user @nya: 19 | 20 | .. image:: radio-transmission-spectrogram.png 21 | 22 | 23 | Image 24 | ----- 25 | 26 | A 2D image created by: 27 | 28 | 1. Converting low and high frequency spans into black and white squares respectively. 29 | 2. Rearranging these squares into a rectangle instead of a single line. 30 | 31 | Contributed by Discord user @elventian. 32 | 33 | .. image:: radio-transmission-2d.png 34 | :width: 240px 35 | 36 | 37 | Interpretation 38 | -------------- 39 | 40 | Based on the discussions with Discord users @nya, @Kilew, @fryguybob, @aaaa1, @gltronred and @elventian. 41 | 42 | Probably the symbols on the left represent numbers and the number of elements on the right is the unary representation of this number. 43 | 44 | Symbols on the left look like a binary encoding that should work for numbers 1..15. Picture says 8, because we have hard data only up to 8: 45 | 46 | .. image:: radio-transmission-encoding1.png 47 | :width: 240px 48 | 49 | According to this theory we can speculate that the numbers 9..15 would be represented with these symbols: 50 | 51 | .. image:: radio-transmission-encoding2.png 52 | :width: 420px 53 | 54 | Based on this logic the symbols could be extended further like this: 55 | 56 | .. image:: radio-transmission-encoding3.png 57 | :width: 560px 58 | 59 | :doc:`Second transmission ` seems to support the second conjecture (right picture). 60 | 61 | 62 | Code 63 | ----------- 64 | 65 | This Rust code generates decoded images similar to the image included above from WAV files. 66 | 67 | Contributed by Discord user @aaaa1. 68 | 69 | Example output: 70 | 71 | .. image:: radio-transmission-2d-rust.png 72 | :width: 100px 73 | 74 | .. literalinclude:: decode-wav.rs 75 | :language: rust 76 | 77 | 78 | .. _edit it on GitHub: https://github.com/zaitsev85/message-from-space/blob/master/source/radio-transmission-recording.rst 79 | .. _notebook: https://gist.github.com/nya3jp/5094571c5905783327f35e8df207c8ad#file-spectrogram-ipynb 80 | .. _Discord chat: https://discord.gg/xvMJbas 81 | -------------------------------------------------------------------------------- /source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | import os 17 | import sphinx_rtd_theme 18 | from PIL import Image 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'Message From Space' 24 | copyright = '2020, Ivan Zaitsev' 25 | author = 'Ivan Zaitsev' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx_rtd_theme', 35 | 'sphinxcontrib.images', 36 | 'sphinxcontrib.rsvgconverter', 37 | 'sphinx.ext.todo', 38 | ] 39 | 40 | todo_include_todos = True 41 | todo_link_only = True 42 | 43 | images_config = { 44 | 'override_image_directive': True, 45 | } 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # List of patterns, relative to source directory, that match files and 51 | # directories to ignore when looking for source files. 52 | # This pattern also affects html_static_path and html_extra_path. 53 | exclude_patterns = [] 54 | 55 | 56 | # -- Options for HTML output ------------------------------------------------- 57 | 58 | # The theme to use for HTML and HTML Help pages. See the documentation for 59 | # a list of builtin themes. 60 | # 61 | html_theme = 'sphinx_rtd_theme' 62 | 63 | # Add any paths that contain custom static files (such as style sheets) here, 64 | # relative to this directory. They are copied after the builtin static files, 65 | # so a file named "default.css" will overwrite the builtin "default.css". 66 | html_static_path = ['_static'] 67 | 68 | # Condensed version generation 69 | def setup(app): 70 | dirname = os.path.dirname(__file__) 71 | 72 | def get_image_width(filename): 73 | img = Image.open(filename) 74 | return img.size[0] 75 | 76 | with open(os.path.join(dirname, 'condensed-version.rst'), 'w') as f: 77 | f.write('Condensed Version\n') 78 | f.write('=================\n\n') 79 | 80 | i = 1 81 | while os.path.isfile(os.path.join(dirname, 'message%d.png' % i)): 82 | with open(os.path.join(dirname, 'message%d.rst' % i)) as m: 83 | title = m.readline() 84 | f.write(title) 85 | f.write('-' * (len(title) - 1) + '\n\n') 86 | 87 | f.write('.. image:: message%d.png\n' % i) 88 | f.write(' :width: %dpx\n\n' % get_image_width(os.path.join(dirname, 'message%d.png' % i))) 89 | 90 | f.write('.. literalinclude:: message%d-decoded.txt\n\n\n' % i) 91 | 92 | i += 1 93 | -------------------------------------------------------------------------------- /source/annotate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #! nix-shell -i python -p "python38.withPackages(p:[p.pillow])" 3 | 4 | import sys 5 | from PIL import Image 6 | 7 | 8 | class Img: 9 | def __init__(self, fname, zoom): 10 | self._img = Image.open(fname) 11 | self._pixels = self._img.load() 12 | self._zoom = zoom 13 | 14 | self.size = self._img.size[0] // zoom, self._img.size[1] // zoom 15 | 16 | def __getitem__(self, xy): 17 | xy = xy[0] * self._zoom, xy[1] * self._zoom 18 | try: 19 | c = self._pixels[xy] 20 | except IndexError: 21 | return False 22 | return c[0] + c[1] + c[2] > 382 23 | 24 | def dump(self, ix, iy, higlight = set()): 25 | for y in iy: 26 | for x in ix: 27 | if (x,y) in higlight: 28 | print(end="\x1b[40;31m") 29 | print(end=".#"[self[x,y]]) 30 | if (x,y) in higlight: 31 | print(end="\x1b[m") 32 | print() 33 | print() 34 | 35 | 36 | class Svg: 37 | def __init__(self, fname, width, height): 38 | self._f = open(fname, "w") 39 | self._print( 40 | f'' 41 | ) 42 | self._print(f'') 43 | 44 | def _print(self, *args, **kwargs): 45 | print(*args, **kwargs, file=self._f) 46 | 47 | def point(self, x, y): 48 | self._print( 49 | f'' 50 | ) 51 | 52 | def annotation(self, x, y, w, h, text): 53 | self._print( 54 | f'' 55 | ) 56 | style = "paint-order: stroke; fill: white; stroke: black; stroke-width: 2px; font:24px bold sans;" 57 | self._print( 58 | f'{text}' 59 | ) 60 | 61 | def close(self): 62 | self._print("") 63 | self._f.close() 64 | 65 | 66 | def decode_number(img, x, y): 67 | if img[x - 1, y - 1] or img[x, y - 1] or img[x - 1, y] or img[x, y]: 68 | return None 69 | 70 | # Get the size by iterating over top and left edges 71 | size = 0 72 | negative = False 73 | while True: 74 | items = ( 75 | img[x + size + 1, y - 1], 76 | img[x + size + 1, y], 77 | img[x - 1, y + size + 1], 78 | img[x, y + size + 1], 79 | ) 80 | if items == (False, True, False, True): 81 | size += 1 82 | continue 83 | if items == (False, False, False, False): 84 | break 85 | if items == (False, False, False, True): 86 | negative = True 87 | break 88 | return None 89 | 90 | if size == 0: 91 | return None 92 | 93 | # Check that right and bottom edges are empty 94 | for i in range(1,size + 2): 95 | if img[x + size + 1, y+i] or img[x+i, y + size + 1]: 96 | return None 97 | 98 | # Decode the number 99 | result, d = 0, 1 100 | for iy in range(size): 101 | for ix in range(size): 102 | result += d * img[x + ix + 1, y + iy + 1] 103 | d *= 2 104 | 105 | if negative: 106 | result = -result 107 | 108 | return (size, size+negative), result 109 | 110 | 111 | def main(in_fname, out_fname): 112 | img = Img(in_fname, 4) 113 | svg = Svg(out_fname, img.size[0], img.size[1]) 114 | 115 | for y in range(img.size[1]): 116 | for x in range(img.size[0]): 117 | if img[x, y]: 118 | svg.point(x, y) 119 | 120 | for y in range(img.size[1]): 121 | for x in range(img.size[0]): 122 | if (n := decode_number(img, x, y)) is not None: 123 | svg.annotation(x - 0.5, y - 0.5, n[0][0] + 2, n[0][1] + 2, n[1]) 124 | svg.close() 125 | 126 | 127 | main(sys.argv[1], sys.argv[2]) 128 | -------------------------------------------------------------------------------- /source/annotate.hs: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env nix-shell 2 | -- Usage: ./annotate.hs in-msg.png out-annotated.svg out-decoded.txt 3 | #! nix-shell -i runhaskell -p 4 | #! nix-shell "haskellPackages.ghcWithPackages (pkgs: with pkgs; [JuicyPixels JuicyPixels-util errors extra groupBy])" 5 | #! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/5cb5ccb54229efd9a4cd1fccc0f43e0bbed81c5d.tar.gz 6 | 7 | import Control.Applicative ((<|>)) 8 | import Control.Error.Safe (assertMay) 9 | import Data.List (foldl', intercalate) 10 | import Data.List.Extra (groupOn) 11 | import Data.List.GroupBy (groupBy) 12 | import Data.Maybe (catMaybes) 13 | import Data.Word (Word16) 14 | import Safe (lastMay) 15 | import System.Environment (getArgs) 16 | import qualified Codec.Picture as P 17 | import qualified Codec.Picture.RGBA8 as P8 18 | 19 | -------------------------------------------------------------------------------- 20 | -- Img 21 | 22 | type Scale = Int 23 | type Coord = (Int, Int) 24 | type Size = (Int, Int) 25 | data Img = Img (P.Image P.PixelRGBA8) Scale 26 | 27 | imgLoad :: FilePath -> Scale -> IO Img 28 | imgLoad path scale = (\img -> Img img scale) <$> P8.readImageRGBA8 path 29 | 30 | imgWidth :: Img -> Int 31 | imgWidth (Img img scale) = P.imageWidth img `div` scale 32 | 33 | imgHeight :: Img -> Int 34 | imgHeight (Img img scale) = P.imageHeight img `div` scale 35 | 36 | imgPixel :: Img -> Coord -> Bool 37 | imgPixel (Img img scale) (x, y) = 38 | if x' < 0 || y' < 0 || x' >= P.imageWidth img || y' >= P.imageHeight img 39 | then False 40 | else fromIntegral r + fromIntegral g + fromIntegral b > (0::Word16) 41 | where 42 | x' = x * scale 43 | y' = y * scale 44 | P.PixelRGBA8 r g b _ = P.pixelAt img x' y' 45 | 46 | imgShow :: Img -> [Int] -> [Int] -> String 47 | imgShow img xs ys = unlines $ map showLine ys 48 | where showLine y = concatMap (\x -> if imgPixel img (x,y) then "#" else ".") xs 49 | 50 | imgShowFull :: Img -> String 51 | imgShowFull img = imgShow img [0..imgWidth img - 1] [0..imgHeight img - 1] 52 | 53 | instance Show Img where 54 | show = imgShowFull 55 | 56 | imgAllPixels :: Img -> [Coord] 57 | imgAllPixels img = [(x, y) | y <- [0..imgHeight img - 1], x <- [0..imgWidth img - 1]] 58 | 59 | imgInnerPixels :: Img -> [Coord] 60 | imgInnerPixels img = [(x, y) | y <- [1..imgHeight img - 2], x <- [1..imgWidth img - 2]] 61 | 62 | -------------------------------------------------------------------------------- 63 | -- Number decoder 64 | 65 | decodeSymbol :: Img -> Coord -> Maybe (Integer, Bool, Size) 66 | decodeSymbol img (x, y) = do 67 | {- 68 | Figure: 69 | . . _ _ _ _ , 70 | . : # # # # , → x 71 | _ # + + + + - 72 | _ # + + + + - 73 | _ # + + + + - 74 | _ # + + + + - 75 | , ? - - - - - 76 | 77 | ↓ 78 | y 79 | 80 | Legend: 81 | . _ , - — black pixels 82 | # — white pixels 83 | ? — negativity bit 84 | + — binary data 85 | : — point (x, y). isOperator bit 86 | -} 87 | 88 | let px = imgPixel img 89 | 90 | -- 1. Check that top left corner is empty (`.`) 91 | assertMay $ not $ any px [(x-1, y-1), (x, y-1), (x-1, y)] 92 | 93 | let isOperator = px (x, y) 94 | 95 | -- 2. Calculate the size based on top and left edges (`_` and `#`) 96 | let topLeft' i = (px (x + i, y - 1), px (x + i, y), 97 | px (x - 1, y + i), px (x, y + i)) 98 | let topLeft = takeWhile (\i -> (False, True, False, True) == topLeft' i) $ [1..] 99 | size <- lastMay topLeft 100 | assertMay $ size >= 1 101 | 102 | -- 3. Check the negativity bit (`?`) and empty space at corners (`,`) 103 | negative <- 104 | case topLeft' (size+1) of 105 | (False, False, False, False) -> Just False 106 | (False, False, False, True) -> Just True 107 | otherwise -> Nothing 108 | 109 | -- 4. Check that right and bottom edges are empty (`-`) 110 | assertMay $ not $ 111 | any (\i -> px (x + size + 1, y+i) || px(x+i, y + size + 1)) [1 .. size+1] 112 | 113 | -- 5. Decode binary data 114 | let number = bitsToInteger [px (ix,iy) | iy <- [y+1 .. y+size], ix <- [x+1 .. x+size]] 115 | 116 | Just $ if negative 117 | then (-number, isOperator, (size+1, size+2)) 118 | else (number, isOperator, (size+1, size+1)) 119 | 120 | bitsToInteger :: [Bool] -> Integer 121 | bitsToInteger = fst . foldl' f (0, 1) 122 | where 123 | f (sum, bit) True = (sum + bit, bit*2) 124 | f (sum, bit) False = (sum, bit*2) 125 | 126 | sym :: [String] -> [[Bool]] 127 | sym = map (map (=='#')) 128 | 129 | symEllipsis = sym 130 | [ "........." 131 | , ".#.#.#.#." 132 | , "........." 133 | ] 134 | 135 | checkLine :: Img -> [Bool] -> Coord -> Bool 136 | checkLine img [] _ = True 137 | checkLine img (h:t) (x, y) = (imgPixel img (x, y) == h) && checkLine img t (x+1, y) 138 | 139 | checkSymbol :: Img -> [[Bool]] -> Coord -> Bool 140 | checkSymbol _ [] _ = True 141 | checkSymbol img (h:t) (x, y) = checkLine img h (x, y) && checkSymbol img t (x, y+1) 142 | 143 | -------------------------------------------------------------------------------- 144 | -- svg 145 | 146 | svg img annotations = 147 | concat $ ( 148 | svgHead img ++ 149 | svgImgPoints img ++ 150 | concatMap (\(coord,size,text,color) -> svgAnnotation coord size text color) annotations ++ 151 | [""] 152 | ) 153 | 154 | svgHead img = [ 155 | "\n", 160 | "\n" 161 | ] 162 | 163 | svgPoint img (x, y) value = [ 164 | "\n" 169 | ] 170 | 171 | svgImgPoints img = 172 | concatMap (\coord -> svgPoint img coord (imgPixel img coord)) $ imgAllPixels img 173 | 174 | svgAnnotation :: Coord -> Size -> String -> String -> [String] 175 | svgAnnotation (x, y) (w, h) text color = [ 176 | "\n", 181 | 182 | "", text, "\n" 187 | ] 188 | 189 | -------------------------------------------------------------------------------- 190 | -- Main 191 | 192 | decodeAnnotate img coord@(x,y) = num <|> symEllipsis' 193 | where 194 | num = do 195 | (value, isOperator, size) <- decodeSymbol img (x, y) 196 | Just $ if isOperator 197 | then (coord, size, showOperator value, "yellow") 198 | else (coord, size, show value, "green") 199 | 200 | showOperator 0 = "ap" 201 | showOperator 12 = "=" 202 | showOperator 401 = "dec" 203 | showOperator 417 = "inc" 204 | showOperator 365 = "add" 205 | 206 | -- TODO: proper way of detecting of variables 207 | showOperator 501 = "x0" 208 | showOperator 485 = "x1" 209 | showOperator 65193 = "x2" 210 | showOperator 65161 = "x3" 211 | showOperator 64745 = "x4" 212 | 213 | showOperator n = ":" ++ show n 214 | 215 | symEllipsis' = do 216 | assertMay $ checkSymbol img symEllipsis (x, y) 217 | Just ((x+1, y+1), (7, 1), "...", "gray") 218 | 219 | annotateImg :: Img -> String 220 | annotateImg img = id 221 | $ svg img 222 | $ catMaybes 223 | $ map (decodeAnnotate img) 224 | $ imgInnerPixels img 225 | 226 | decodeImg :: Img -> String 227 | decodeImg img = id 228 | $ unlines 229 | $ map (intercalate " ") -- join groups 230 | $ map (map (intercalate " ")) -- join items inside each group 231 | $ map (map (map (\(_,_,text,_) -> text))) 232 | $ map (groupBy (\a b -> xRight a >= xLeft b - 2)) -- split by horisontal groups 233 | $ groupOn (\((_,y),_,_,_) -> y) -- split by lines 234 | $ catMaybes 235 | $ map (decodeAnnotate img) 236 | $ imgInnerPixels img 237 | where 238 | xLeft ((x,_),(_,_),_,_) = x 239 | xRight ((x,_),(w,_),_,_) = x + w 240 | 241 | main = do 242 | [fnameIn, fnameSvg, fnameTxt] <- getArgs 243 | img <- imgLoad fnameIn 4 244 | writeFile fnameSvg $ annotateImg img 245 | writeFile fnameTxt $ decodeImg img 246 | -------------------------------------------------------------------------------- /source/message4-annotated.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 0 328 | 329 | 0 330 | 331 | 1 332 | 333 | 1 334 | 335 | 2 336 | 337 | 2 338 | 339 | 3 340 | 341 | 3 342 | 343 | 10 344 | 345 | 10 346 | 347 | 11 348 | 349 | 11 350 | 351 | -1 352 | 353 | -1 354 | 355 | -2 356 | 357 | -2 358 | 359 | -------------------------------------------------------------------------------- /source/message3-annotated.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 4 719 | 720 | 3 721 | 722 | 2 723 | 724 | 1 725 | 726 | 0 727 | 728 | -------------------------------------------------------------------------------- /source/message3-annotated-full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 4 719 | 720 | 3 721 | 722 | 2 723 | 724 | 1 725 | 726 | 0 727 | 728 | -1 729 | 730 | -2 731 | 732 | -3 733 | 734 | -4 735 | 736 | -5 737 | 738 | -6 739 | 740 | -7 741 | 742 | -8 743 | 744 | -9 745 | 746 | -10 747 | 748 | -11 749 | 750 | -12 751 | 752 | -13 753 | 754 | -14 755 | 756 | -15 757 | 758 | -16 759 | 760 | -17 761 | 762 | -510 763 | 764 | -511 765 | 766 | -512 767 | 768 | -513 769 | 770 | -514 771 | 772 | -65535 773 | 774 | -65536 775 | 776 | -65537 777 | 778 | --------------------------------------------------------------------------------