├── .gitignore ├── LICENSE ├── README.md ├── bitstring-3.0.2 ├── PKG-INFO ├── README.txt ├── bitstring.py ├── build │ └── lib.linux-x86_64-2.7 │ │ └── bitstring.py ├── doc │ └── bitstring_manual.pdf ├── release_notes.txt ├── setup.py └── test │ ├── smalltestfile │ ├── test.m1v │ ├── test_bitarray.py │ ├── test_bits.py │ ├── test_bitstore.py │ ├── test_bitstream.py │ ├── test_bitstring.py │ └── test_constbitstream.py ├── docs ├── A3967.pdf ├── EasyDriver_v43.pdf ├── matrix_method_rev_e.pdf └── stepper_datasheet.pdf ├── images ├── General_sch.jpg ├── gui.jpg ├── parts │ ├── ArduinoDuemilanove.jpg │ ├── easy_driver.jpg │ ├── laser_img.jpg │ ├── pf35t_MED.jpg │ └── unipolar_stepper_sch.png ├── photo_1.jpg ├── photo_1_small.jpg └── photo_2.jpg ├── main ├── README.md ├── arduino │ └── plaser │ │ ├── AxesLib.cpp │ │ ├── AxesLib.h │ │ ├── CoordsLib.cpp │ │ ├── CoordsLib.h │ │ └── plaser.pde └── python │ ├── coords.py │ ├── gen.sh │ ├── laser_control_main.py │ ├── ldevice.py │ ├── repeat_timer.py │ ├── telescope_server.py │ └── ui │ ├── __init__.py │ ├── icons │ ├── down.png │ ├── left.png │ ├── right.png │ └── up.png │ ├── icons_set.qrc │ ├── icons_set_rc.py │ ├── laser_control.ui │ └── laser_control_ui.py └── testing ├── device ├── README.md ├── arduino │ └── example │ │ ├── CoordsLib.cpp │ │ ├── CoordsLib.h │ │ └── example.pde └── testing.py └── stellarium ├── README.md ├── coords.py └── telescope_server.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Juan Ramón 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Telescope control with Stellarium and Arduino 2 | ============================================== 3 | 4 | 5 | This project consists on a first approach to control a telescope mechanism, builded from scratch and 6 | based on Arduino microcontroller, from a computer **with GNU/Linux** and the Stellarium software. 7 | 8 | By the moment, the code has been tested only in a mini-dobsonian mount with a green laser pointer instead 9 | of a real telescope. The laser points towards the celestial objects indicated from Stellarium, by using a 10 | stepper motors system. 11 | 12 | ![Device](https://raw.github.com/juanrmn/Arduino-Telescope-Control/master/images/photo_1_small.jpg) 13 | 14 | You can see more images, schematics and parts details in the images folder. 15 | 16 | 17 | Software 18 | --------- 19 | 20 | The software is divided on two main blocks, the first one implemented in Python (computer) and the other 21 | one for Arduino microcontroller (device): 22 | 23 | In the computer side with Python: 24 | 25 | * Communications with Stellarium ([Stellarium Telescope Protocol](http://www.stellarium.org/wiki/index.php/Telescope_Control_(client-server\)), [python-bitstring](http://code.google.com/p/python-bitstring/))) 26 | * Communications with the device (USB-Serial) 27 | * User interface (PyQt4) 28 | 29 | Device with Arduino: 30 | 31 | * Communications with the computer (receiving commands and parameters, and sending responses) 32 | * Coordinate transformations (based on Toshimi Taki's [Matrix Method for Coodinates Transformation](http://www.geocities.jp/toshimi_taki/matrix/matrix.htm)) 33 | * Control mechanisms (stepper motors, sensors, positioning..) 34 | 35 | 36 | 37 | ### testing folder 38 | 39 | Contains the necessary scripts for testing isolately the communications with Stellarium, communications with the device, 40 | and the Arduino library for coordinate transformation (Matrix Method implementation). 41 | 42 | 43 | ### main folder 44 | 45 | Software to control the "Laser pointer device" with Stellarium, including GUI. 46 | 47 | 48 | ### docs folder 49 | 50 | Datasheets and Matrix method documentation. 51 | 52 | 53 | ### bitstring-3.0.2 folder 54 | 55 | A Python module that makes the creation, manipulation and analysis of binary data as simple and natural as possible. 56 | 57 | Bitstring project page: http://code.google.com/p/python-bitstring/ 58 | 59 | 60 | -------------------------------------------------------------------------------- /bitstring-3.0.2/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: bitstring 3 | Version: 3.0.2 4 | Summary: Simple construction, analysis and modification of binary data. 5 | Home-page: http://python-bitstring.googlecode.com 6 | Author: Scott Griffiths 7 | Author-email: scott@griffiths.name 8 | License: The MIT License: http://www.opensource.org/licenses/mit-license.php 9 | Download-URL: http://python-bitstring.googlecode.com 10 | Description: ================ 11 | bitstring module 12 | ================ 13 | 14 | **bitstring** is a pure Python module designed to help make 15 | the creation and analysis of binary data as simple and natural as possible. 16 | 17 | Bitstrings can be constructed from integers (big and little endian), hex, 18 | octal, binary, strings or files. They can be sliced, joined, reversed, 19 | inserted into, overwritten, etc. with simple functions or slice notation. 20 | They can also be read from, searched and replaced, and navigated in, 21 | similar to a file or stream. 22 | 23 | bitstring is open source software, and has been released under the MIT 24 | licence. 25 | 26 | This version supports Python 2.6 and later (including Python 3). 27 | For Python 2.4 and 2.5 you should instead download version 1.0. 28 | 29 | Documentation 30 | ------------- 31 | The manual for the bitstring module is available here 32 | . It contains a walk-through of all 33 | the features and a complete reference section. 34 | 35 | It is also available as a PDF as part of the source download. 36 | 37 | Installation 38 | ------------ 39 | If you have downloaded and unzipped the package then you need to run the 40 | ``setup.py`` script with the 'install' argument:: 41 | 42 | python setup.py install 43 | 44 | You may need to run this with root privileges on Unix-like systems. 45 | 46 | 47 | If you haven't yet downloaded the package then you can just try:: 48 | 49 | easy_install bitstring 50 | 51 | or :: 52 | 53 | pip install bitstring 54 | 55 | If you're using Windows then there is an installer available from the 56 | downloads tab on the project's homepage. 57 | 58 | Simple Examples 59 | --------------- 60 | Creation:: 61 | 62 | >>> a = BitArray(bin='00101') 63 | >>> b = Bits(a_file_object) 64 | >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22') 65 | >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f') 66 | >>> e = pack('<16h', *range(16)) 67 | 68 | Different interpretations, slicing and concatenation:: 69 | 70 | >>> a = BitArray('0x1af') 71 | >>> a.hex, a.bin, a.uint 72 | ('1af', '000110101111', 431) 73 | >>> a[10:3:-1].bin 74 | '1110101' 75 | >>> 3*a + '0b100' 76 | BitArray('0o0657056705674') 77 | 78 | Reading data sequentially:: 79 | 80 | >>> b = BitStream('0x160120f') 81 | >>> b.read(12).hex 82 | '160' 83 | >>> b.pos = 0 84 | >>> b.read('uint:12') 85 | 352 86 | >>> b.readlist('uint:12, bin:3') 87 | [288, '111'] 88 | 89 | Searching, inserting and deleting:: 90 | 91 | >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' 92 | >>> c.find('0x48') 93 | (8,) 94 | >>> c.replace('0b001', '0xabc') 95 | >>> c.insert('0b0000') 96 | >>> del c[12:16] 97 | 98 | Unit Tests 99 | ---------- 100 | 101 | The 400+ unit tests should all pass for Python 2.6 and later. 102 | 103 | ---- 104 | 105 | The bitstring module has been released as open source under the MIT License. 106 | Copyright (c) 2012 Scott Griffiths 107 | 108 | For more information see the project's homepage on Google Code: 109 | 110 | 111 | 112 | Platform: all 113 | Classifier: Development Status :: 5 - Production/Stable 114 | Classifier: Intended Audience :: Developers 115 | Classifier: Operating System :: OS Independent 116 | Classifier: License :: OSI Approved :: MIT License 117 | Classifier: Programming Language :: Python :: 2.6 118 | Classifier: Programming Language :: Python :: 2.7 119 | Classifier: Programming Language :: Python :: 3 120 | Classifier: Programming Language :: Python :: 3.0 121 | Classifier: Programming Language :: Python :: 3.1 122 | Classifier: Programming Language :: Python :: 3.2 123 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 124 | -------------------------------------------------------------------------------- /bitstring-3.0.2/README.txt: -------------------------------------------------------------------------------- 1 | ================ 2 | bitstring module 3 | ================ 4 | 5 | **bitstring** is a pure Python module designed to help make 6 | the creation and analysis of binary data as simple and natural as possible. 7 | 8 | Bitstrings can be constructed from integers (big and little endian), hex, 9 | octal, binary, strings or files. They can be sliced, joined, reversed, 10 | inserted into, overwritten, etc. with simple functions or slice notation. 11 | They can also be read from, searched and replaced, and navigated in, 12 | similar to a file or stream. 13 | 14 | bitstring is open source software, and has been released under the MIT 15 | licence. 16 | 17 | This version supports Python 2.6 and later (including Python 3). 18 | For Python 2.4 and 2.5 you should instead download version 1.0. 19 | 20 | Documentation 21 | ------------- 22 | The manual for the bitstring module is available here 23 | . It contains a walk-through of all 24 | the features and a complete reference section. 25 | 26 | It is also available as a PDF as part of the source download. 27 | 28 | Installation 29 | ------------ 30 | If you have downloaded and unzipped the package then you need to run the 31 | ``setup.py`` script with the 'install' argument:: 32 | 33 | python setup.py install 34 | 35 | You may need to run this with root privileges on Unix-like systems. 36 | 37 | 38 | If you haven't yet downloaded the package then you can just try:: 39 | 40 | easy_install bitstring 41 | 42 | or :: 43 | 44 | pip install bitstring 45 | 46 | If you're using Windows then there is an installer available from the 47 | downloads tab on the project's homepage. 48 | 49 | Simple Examples 50 | --------------- 51 | Creation:: 52 | 53 | >>> a = BitArray(bin='00101') 54 | >>> b = Bits(a_file_object) 55 | >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22') 56 | >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f') 57 | >>> e = pack('<16h', *range(16)) 58 | 59 | Different interpretations, slicing and concatenation:: 60 | 61 | >>> a = BitArray('0x1af') 62 | >>> a.hex, a.bin, a.uint 63 | ('1af', '000110101111', 431) 64 | >>> a[10:3:-1].bin 65 | '1110101' 66 | >>> 3*a + '0b100' 67 | BitArray('0o0657056705674') 68 | 69 | Reading data sequentially:: 70 | 71 | >>> b = BitStream('0x160120f') 72 | >>> b.read(12).hex 73 | '160' 74 | >>> b.pos = 0 75 | >>> b.read('uint:12') 76 | 352 77 | >>> b.readlist('uint:12, bin:3') 78 | [288, '111'] 79 | 80 | Searching, inserting and deleting:: 81 | 82 | >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' 83 | >>> c.find('0x48') 84 | (8,) 85 | >>> c.replace('0b001', '0xabc') 86 | >>> c.insert('0b0000') 87 | >>> del c[12:16] 88 | 89 | Unit Tests 90 | ---------- 91 | 92 | The 400+ unit tests should all pass for Python 2.6 and later. 93 | 94 | ---- 95 | 96 | The bitstring module has been released as open source under the MIT License. 97 | Copyright (c) 2012 Scott Griffiths 98 | 99 | For more information see the project's homepage on Google Code: 100 | 101 | 102 | -------------------------------------------------------------------------------- /bitstring-3.0.2/doc/bitstring_manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/bitstring-3.0.2/doc/bitstring_manual.pdf -------------------------------------------------------------------------------- /bitstring-3.0.2/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from distutils.core import setup 3 | #from distutils.extension import Extension 4 | #from Cython.Distutils import build_ext 5 | import sys 6 | 7 | kwds = {'long_description': open('README.txt').read()} 8 | 9 | if sys.version_info[:2] < (2, 6): 10 | raise Exception('This version of bitstring needs Python 2.6 or later. ' 11 | 'For Python 2.4 / 2.5 please use bitstring version 1.0 instead.') 12 | 13 | #macros = [('PYREX_WITHOUT_ASSERTIONS', None)] 14 | #ext_modules = [Extension('bitstring', ["bitstring.py"], define_macros=macros)] 15 | 16 | setup(name='bitstring', 17 | version='3.0.2', 18 | description='Simple construction, analysis and modification of binary data.', 19 | author='Scott Griffiths', 20 | author_email='scott@griffiths.name', 21 | url='http://python-bitstring.googlecode.com', 22 | download_url='http://python-bitstring.googlecode.com', 23 | license='The MIT License: http://www.opensource.org/licenses/mit-license.php', 24 | # cmdclass = {'build_ext': build_ext}, 25 | # ext_modules = ext_modules, 26 | py_modules=['bitstring'], 27 | platforms='all', 28 | classifiers = [ 29 | 'Development Status :: 5 - Production/Stable', 30 | 'Intended Audience :: Developers', 31 | 'Operating System :: OS Independent', 32 | 'License :: OSI Approved :: MIT License', 33 | 'Programming Language :: Python :: 2.6', 34 | 'Programming Language :: Python :: 2.7', 35 | 'Programming Language :: Python :: 3', 36 | 'Programming Language :: Python :: 3.0', 37 | 'Programming Language :: Python :: 3.1', 38 | 'Programming Language :: Python :: 3.2', 39 | 'Topic :: Software Development :: Libraries :: Python Modules', 40 | ], 41 | **kwds 42 | ) 43 | 44 | -------------------------------------------------------------------------------- /bitstring-3.0.2/test/smalltestfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/bitstring-3.0.2/test/smalltestfile -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test.m1v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/bitstring-3.0.2/test/test.m1v -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test_bitarray.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Unit tests for the bitarray module. 4 | """ 5 | 6 | import unittest 7 | import sys 8 | 9 | sys.path.insert(0, '..') 10 | import bitstring 11 | from bitstring import BitArray 12 | 13 | class All(unittest.TestCase): 14 | def testCreationFromUint(self): 15 | s = BitArray(uint=15, length=6) 16 | self.assertEqual(s.bin, '001111') 17 | s = BitArray(uint=0, length=1) 18 | self.assertEqual(s.bin, '0') 19 | s.uint = 1 20 | self.assertEqual(s.uint, 1) 21 | s = BitArray(length=8) 22 | s.uint = 0 23 | self.assertEqual(s.uint, 0) 24 | s.uint = 255 25 | self.assertEqual(s.uint, 255) 26 | self.assertEqual(s.len, 8) 27 | self.assertRaises(bitstring.CreationError, s._setuint, 256) 28 | 29 | def testCreationFromOct(self): 30 | s = BitArray(oct='7') 31 | self.assertEqual(s.oct, '7') 32 | self.assertEqual(s.bin, '111') 33 | s.append('0o1') 34 | self.assertEqual(s.bin, '111001') 35 | s.oct = '12345670' 36 | self.assertEqual(s.length, 24) 37 | self.assertEqual(s.bin, '001010011100101110111000') 38 | s = BitArray('0o123') 39 | self.assertEqual(s.oct, '123') 40 | 41 | 42 | class NoPosAttribute(unittest.TestCase): 43 | def testReplace(self): 44 | s = BitArray('0b01') 45 | s.replace('0b1', '0b11') 46 | self.assertEqual(s, '0b011') 47 | 48 | def testDelete(self): 49 | s = BitArray('0b000000001') 50 | del s[-1:] 51 | self.assertEqual(s, '0b00000000') 52 | 53 | def testInsert(self): 54 | s = BitArray('0b00') 55 | s.insert('0xf', 1) 56 | self.assertEqual(s, '0b011110') 57 | 58 | def testInsertParameters(self): 59 | s = BitArray('0b111') 60 | self.assertRaises(TypeError, s.insert, '0x4') 61 | 62 | def testOverwrite(self): 63 | s = BitArray('0b01110') 64 | s.overwrite('0b000', 1) 65 | self.assertEqual(s, '0b00000') 66 | 67 | def testOverwriteParameters(self): 68 | s = BitArray('0b0000') 69 | self.assertRaises(TypeError, s.overwrite, '0b111') 70 | 71 | def testPrepend(self): 72 | s = BitArray('0b0') 73 | s.prepend([1]) 74 | self.assertEqual(s, [1, 0]) 75 | 76 | def testRol(self): 77 | s = BitArray('0b0001') 78 | s.rol(1) 79 | self.assertEqual(s, '0b0010') 80 | 81 | def testRor(self): 82 | s = BitArray('0b1000') 83 | s.ror(1) 84 | self.assertEqual(s, '0b0100') 85 | 86 | def testSetItem(self): 87 | s = BitArray('0b000100') 88 | s[4:5] = '0xf' 89 | self.assertEqual(s, '0b000111110') 90 | s[0:1] = [1] 91 | self.assertEqual(s, '0b100111110') 92 | 93 | 94 | class Bugs(unittest.TestCase): 95 | def testAddingNonsense(self): 96 | a = BitArray([0]) 97 | a += '0' # a uint of length 0 - so nothing gets added. 98 | self.assertEqual(a, [0]) 99 | self.assertRaises(ValueError, a.__iadd__, '3') 100 | self.assertRaises(ValueError, a.__iadd__, 'se') 101 | self.assertRaises(ValueError, a.__iadd__, 'float:32') 102 | 103 | 104 | class ByteAligned(unittest.TestCase): 105 | def testDefault(self, defaultbytealigned=bitstring.bytealigned): 106 | self.assertFalse(defaultbytealigned) 107 | 108 | def testChangingIt(self): 109 | bitstring.bytealigned = True 110 | self.assertTrue(bitstring.bytealigned) 111 | bitstring.bytealigned = False 112 | 113 | def testNotByteAligned(self): 114 | bitstring.bytealigned = False 115 | a = BitArray('0x00 ff 0f f') 116 | l = list(a.findall('0xff')) 117 | self.assertEqual(l, [8, 20]) 118 | p = a.find('0x0f')[0] 119 | self.assertEqual(p, 4) 120 | p = a.rfind('0xff')[0] 121 | self.assertEqual(p, 20) 122 | s = list(a.split('0xff')) 123 | self.assertEqual(s, ['0x00', '0xff0', '0xff']) 124 | a.replace('0xff', '') 125 | self.assertEqual(a, '0x000') 126 | 127 | def testByteAligned(self): 128 | bitstring.bytealigned = True 129 | a = BitArray('0x00 ff 0f f') 130 | l = list(a.findall('0xff')) 131 | self.assertEqual(l, [8]) 132 | p = a.find('0x0f')[0] 133 | self.assertEqual(p, 16) 134 | p = a.rfind('0xff')[0] 135 | self.assertEqual(p, 8) 136 | s = list(a.split('0xff')) 137 | self.assertEqual(s, ['0x00', '0xff0ff']) 138 | a.replace('0xff', '') 139 | self.assertEqual(a, '0x000ff') 140 | 141 | 142 | class SliceAssignment(unittest.TestCase): 143 | 144 | def testSliceAssignmentSingleBit(self): 145 | a = BitArray('0b000') 146 | a[2] = '0b1' 147 | self.assertEqual(a.bin, '001') 148 | a[0] = BitArray(bin='1') 149 | self.assertEqual(a.bin, '101') 150 | a[-1] = '0b0' 151 | self.assertEqual(a.bin, '100') 152 | a[-3] = '0b0' 153 | self.assertEqual(a.bin, '000') 154 | 155 | def testSliceAssignmentSingleBitErrors(self): 156 | a = BitArray('0b000') 157 | self.assertRaises(IndexError, a.__setitem__, -4, '0b1') 158 | self.assertRaises(IndexError, a.__setitem__, 3, '0b1') 159 | self.assertRaises(TypeError, a.__setitem__, 1, 1.3) 160 | 161 | def testSliceAssignmentMulipleBits(self): 162 | a = BitArray('0b0') 163 | a[0] = '0b110' 164 | self.assertEqual(a.bin, '110') 165 | a[0] = '0b000' 166 | self.assertEqual(a.bin, '00010') 167 | a[0:3] = '0b111' 168 | self.assertEqual(a.bin, '11110') 169 | a[-2:] = '0b011' 170 | self.assertEqual(a.bin, '111011') 171 | a[:] = '0x12345' 172 | self.assertEqual(a.hex, '12345') 173 | a[:] = '' 174 | self.assertFalse(a) 175 | 176 | def testSliceAssignmentMultipleBitsErrors(self): 177 | a = BitArray() 178 | self.assertRaises(IndexError, a.__setitem__, 0, '0b00') 179 | a += '0b1' 180 | a[0:2] = '0b11' 181 | self.assertEqual(a, '0b11') 182 | 183 | def testDelSliceStep(self): 184 | a = BitArray(bin='100111101001001110110100101') 185 | del a[::2] 186 | self.assertEqual(a.bin, '0110010101100') 187 | del a[3:9:3] 188 | self.assertEqual(a.bin, '01101101100') 189 | del a[2:7:1] 190 | self.assertEqual(a.bin, '011100') 191 | del a[::99] 192 | self.assertEqual(a.bin, '11100') 193 | del a[::1] 194 | self.assertEqual(a.bin, '') 195 | 196 | def testDelSliceNegativeStep(self): 197 | a = BitArray('0b0001011101101100100110000001') 198 | del a[5:23:-3] 199 | self.assertEqual(a.bin, '0001011101101100100110000001') 200 | del a[25:3:-3] 201 | self.assertEqual(a.bin, '00011101010000100001') 202 | del a[:6:-7] 203 | self.assertEqual(a.bin, '000111010100010000') 204 | del a[15::-2] 205 | self.assertEqual(a.bin, '0010000000') 206 | del a[::-1] 207 | self.assertEqual(a.bin, '') 208 | 209 | def testDelSliceErrors(self): 210 | a = BitArray(10) 211 | del a[5:3] 212 | self.assertEqual(a, 10) 213 | del a[3:5:-1] 214 | self.assertEqual(a, 10) 215 | 216 | def testDelSingleElement(self): 217 | a = BitArray('0b0010011') 218 | del a[-1] 219 | self.assertEqual(a.bin, '001001') 220 | del a[2] 221 | self.assertEqual(a.bin, '00001') 222 | try: 223 | del a[5] 224 | self.assertTrue(False) 225 | except IndexError: 226 | pass 227 | 228 | def testSetSliceStep(self): 229 | a = BitArray(bin='0000000000') 230 | a[::2] = '0b11111' 231 | self.assertEqual(a.bin, '1010101010') 232 | a[4:9:3] = [0, 0] 233 | self.assertEqual(a.bin, '1010001010') 234 | a[7:3:-1] = [1, 1, 1, 0] 235 | self.assertEqual(a.bin, '1010011110') 236 | a[7:1:-2] = [0, 0, 1] 237 | self.assertEqual(a.bin, '1011001010') 238 | a[::-5] = [1, 1] 239 | self.assertEqual(a.bin, '1011101011') 240 | a[::-1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 241 | self.assertEqual(a.bin, '1000000000') 242 | 243 | def testSetSliceErrors(self): 244 | a = BitArray(8) 245 | try: 246 | a[::3] = [1] 247 | self.assertTrue(False) 248 | except ValueError: 249 | pass 250 | class A(object): pass 251 | try: 252 | a[1:2] = A() 253 | self.assertTrue(False) 254 | except TypeError: 255 | pass 256 | try: 257 | a[1:4:-1] = [1, 2] 258 | self.assertTrue(False) 259 | except ValueError: 260 | pass 261 | 262 | 263 | class Subclassing(unittest.TestCase): 264 | 265 | def testIsInstance(self): 266 | class SubBits(BitArray): pass 267 | a = SubBits() 268 | self.assertTrue(isinstance(a, SubBits)) 269 | 270 | def testClassType(self): 271 | class SubBits(BitArray): pass 272 | self.assertEqual(SubBits().__class__, SubBits) 273 | -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test_bits.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import sys 5 | 6 | sys.path.insert(0, '..') 7 | import bitstring 8 | from bitstring import MmapByteArray 9 | from bitstring import Bits, BitArray 10 | 11 | class Creation(unittest.TestCase): 12 | def testCreationFromBytes(self): 13 | s = Bits(bytes=b'\xa0\xff') 14 | self.assertEqual((s.len, s.hex), (16, 'a0ff')) 15 | s = Bits(bytes=b'abc', length=0) 16 | self.assertEqual(s, '') 17 | 18 | def testCreationFromBytesErrors(self): 19 | self.assertRaises(bitstring.CreationError, Bits, bytes=b'abc', length=25) 20 | 21 | def testCreationFromDataWithOffset(self): 22 | s1 = Bits(bytes=b'\x0b\x1c\x2f', offset=0, length=20) 23 | s2 = Bits(bytes=b'\xa0\xb1\xC2', offset=4) 24 | self.assertEqual((s2.len, s2.hex), (20, '0b1c2')) 25 | self.assertEqual((s1.len, s1.hex), (20, '0b1c2')) 26 | self.assertTrue(s1 == s2) 27 | 28 | def testCreationFromHex(self): 29 | s = Bits(hex='0xA0ff') 30 | self.assertEqual((s.len, s.hex), (16, 'a0ff')) 31 | s = Bits(hex='0x0x0X') 32 | self.assertEqual((s.length, s.hex), (0, '')) 33 | 34 | def testCreationFromHexWithWhitespace(self): 35 | s = Bits(hex=' \n0 X a 4e \r3 \n') 36 | self.assertEqual(s.hex, 'a4e3') 37 | 38 | def testCreationFromHexErrors(self): 39 | self.assertRaises(bitstring.CreationError, Bits, hex='0xx0') 40 | self.assertRaises(bitstring.CreationError, Bits, hex='0xX0') 41 | self.assertRaises(bitstring.CreationError, Bits, hex='0Xx0') 42 | self.assertRaises(bitstring.CreationError, Bits, hex='-2e') 43 | # These really should fail, but it's awkward and not a big deal... 44 | # self.assertRaises(bitstring.CreationError, Bits, '0x2', length=2) 45 | # self.assertRaises(bitstring.CreationError, Bits, '0x3', offset=1) 46 | 47 | def testCreationFromBin(self): 48 | s = Bits(bin='1010000011111111') 49 | self.assertEqual((s.length, s.hex), (16, 'a0ff')) 50 | s = Bits(bin='00')[:1] 51 | self.assertEqual(s.bin, '0') 52 | s = Bits(bin=' 0000 \n 0001\r ') 53 | self.assertEqual(s.bin, '00000001') 54 | 55 | def testCreationFromBinWithWhitespace(self): 56 | s = Bits(bin=' \r\r\n0 B 00 1 1 \t0 ') 57 | self.assertEqual(s.bin, '00110') 58 | 59 | def testCreationFromOctErrors(self): 60 | s = Bits('0b00011') 61 | self.assertRaises(bitstring.InterpretError, s._getoct) 62 | self.assertRaises(bitstring.CreationError, s._setoct, '8') 63 | 64 | def testCreationFromUintWithOffset(self): 65 | self.assertRaises(bitstring.Error, Bits, uint=12, length=8, offset=1) 66 | 67 | def testCreationFromUintErrors(self): 68 | self.assertRaises(bitstring.CreationError, Bits, uint=-1, length=10) 69 | self.assertRaises(bitstring.CreationError, Bits, uint=12) 70 | self.assertRaises(bitstring.CreationError, Bits, uint=4, length=2) 71 | self.assertRaises(bitstring.CreationError, Bits, uint=0, length=0) 72 | self.assertRaises(bitstring.CreationError, Bits, uint=12, length=-12) 73 | 74 | def testCreationFromInt(self): 75 | s = Bits(int=0, length=4) 76 | self.assertEqual(s.bin, '0000') 77 | s = Bits(int=1, length=2) 78 | self.assertEqual(s.bin, '01') 79 | s = Bits(int=-1, length=11) 80 | self.assertEqual(s.bin, '11111111111') 81 | s = Bits(int=12, length=7) 82 | self.assertEqual(s.int, 12) 83 | s = Bits(int=-243, length=108) 84 | self.assertEqual((s.int, s.length), (-243, 108)) 85 | for length in range(6, 10): 86 | for value in range(-17, 17): 87 | s = Bits(int=value, length=length) 88 | self.assertEqual((s.int, s.length), (value, length)) 89 | s = Bits(int=10, length=8) 90 | 91 | def testCreationFromIntErrors(self): 92 | self.assertRaises(bitstring.CreationError, Bits, int=-1, length=0) 93 | self.assertRaises(bitstring.CreationError, Bits, int=12) 94 | self.assertRaises(bitstring.CreationError, Bits, int=4, length=3) 95 | self.assertRaises(bitstring.CreationError, Bits, int=-5, length=3) 96 | 97 | def testCreationFromSe(self): 98 | for i in range(-100, 10): 99 | s = Bits(se=i) 100 | self.assertEqual(s.se, i) 101 | 102 | def testCreationFromSeWithOffset(self): 103 | self.assertRaises(bitstring.CreationError, Bits, se=-13, offset=1) 104 | 105 | def testCreationFromSeErrors(self): 106 | self.assertRaises(bitstring.CreationError, Bits, se=-5, length=33) 107 | s = Bits(bin='001000') 108 | self.assertRaises(bitstring.InterpretError, s._getse) 109 | 110 | def testCreationFromUe(self): 111 | [self.assertEqual(Bits(ue=i).ue, i) for i in range(0, 20)] 112 | 113 | def testCreationFromUeWithOffset(self): 114 | self.assertRaises(bitstring.CreationError, Bits, ue=104, offset=2) 115 | 116 | def testCreationFromUeErrors(self): 117 | self.assertRaises(bitstring.CreationError, Bits, ue=-1) 118 | self.assertRaises(bitstring.CreationError, Bits, ue=1, length=12) 119 | s = Bits(bin='10') 120 | self.assertRaises(bitstring.InterpretError, s._getue) 121 | 122 | def testCreationFromBool(self): 123 | a = Bits('bool=1') 124 | self.assertEqual(a, 'bool=1') 125 | b = Bits('bool=0') 126 | self.assertEqual(b, [0]) 127 | c = bitstring.pack('2*bool', 0, 1) 128 | self.assertEqual(c, '0b01') 129 | 130 | def testCreationKeywordError(self): 131 | self.assertRaises(bitstring.CreationError, Bits, squirrel=5) 132 | 133 | def testDataStoreType(self): 134 | a = Bits('0xf') 135 | self.assertEqual(type(a._datastore), bitstring.ConstByteStore) 136 | 137 | 138 | class Initialisation(unittest.TestCase): 139 | def testEmptyInit(self): 140 | a = Bits() 141 | self.assertEqual(a, '') 142 | 143 | def testNoPos(self): 144 | a = Bits('0xabcdef') 145 | try: 146 | a.pos 147 | except AttributeError: 148 | pass 149 | else: 150 | assert False 151 | 152 | def testFind(self): 153 | a = Bits('0xabcd') 154 | r = a.find('0xbc') 155 | self.assertEqual(r[0], 4) 156 | r = a.find('0x23462346246', bytealigned=True) 157 | self.assertFalse(r) 158 | 159 | def testRfind(self): 160 | a = Bits('0b11101010010010') 161 | b = a.rfind('0b010') 162 | self.assertEqual(b[0], 11) 163 | 164 | def testFindAll(self): 165 | a = Bits('0b0010011') 166 | b = list(a.findall([1])) 167 | self.assertEqual(b, [2, 5, 6]) 168 | 169 | 170 | class Cut(unittest.TestCase): 171 | def testCut(self): 172 | s = Bits(30) 173 | for t in s.cut(3): 174 | self.assertEqual(t, [0] * 3) 175 | 176 | 177 | class InterleavedExpGolomb(unittest.TestCase): 178 | def testCreation(self): 179 | s1 = Bits(uie=0) 180 | s2 = Bits(uie=1) 181 | self.assertEqual(s1, [1]) 182 | self.assertEqual(s2, [0, 0, 1]) 183 | s1 = Bits(sie=0) 184 | s2 = Bits(sie=-1) 185 | s3 = Bits(sie=1) 186 | self.assertEqual(s1, [1]) 187 | self.assertEqual(s2, [0, 0, 1, 1]) 188 | self.assertEqual(s3, [0, 0, 1, 0]) 189 | 190 | def testCreationFromProperty(self): 191 | s = BitArray() 192 | s.uie = 45 193 | self.assertEqual(s.uie, 45) 194 | s.sie = -45 195 | self.assertEqual(s.sie, -45) 196 | 197 | def testInterpretation(self): 198 | for x in range(101): 199 | self.assertEqual(Bits(uie=x).uie, x) 200 | for x in range(-100, 100): 201 | self.assertEqual(Bits(sie=x).sie, x) 202 | 203 | def testErrors(self): 204 | for f in ['sie=100, 0b1001', '0b00', 'uie=100, 0b1001']: 205 | s = Bits(f) 206 | self.assertRaises(bitstring.InterpretError, s._getsie) 207 | self.assertRaises(bitstring.InterpretError, s._getuie) 208 | self.assertRaises(ValueError, Bits, 'uie=-10') 209 | 210 | 211 | class FileBased(unittest.TestCase): 212 | def setUp(self): 213 | self.a = Bits(filename='smalltestfile') 214 | self.b = Bits(filename='smalltestfile', offset=16) 215 | self.c = Bits(filename='smalltestfile', offset=20, length=16) 216 | self.d = Bits(filename='smalltestfile', offset=20, length=4) 217 | 218 | def testCreationWithOffset(self): 219 | self.assertEqual(self.a, '0x0123456789abcdef') 220 | self.assertEqual(self.b, '0x456789abcdef') 221 | self.assertEqual(self.c, '0x5678') 222 | 223 | def testBitOperators(self): 224 | x = self.b[4:20] 225 | self.assertEqual(x, '0x5678') 226 | self.assertEqual((x & self.c).hex, self.c.hex) 227 | self.assertEqual(self.c ^ self.b[4:20], 16) 228 | self.assertEqual(self.a[23:36] | self.c[3:], self.c[3:]) 229 | 230 | def testAddition(self): 231 | h = self.d + '0x1' 232 | x = self.a[20:24] + self.c[-4:] + self.c[8:12] 233 | self.assertEqual(x, '0x587') 234 | x = self.b + x 235 | self.assertEqual(x.hex, '456789abcdef587') 236 | x = BitArray(x) 237 | del x[12:24] 238 | self.assertEqual(x, '0x456abcdef587') 239 | 240 | class Mmap(unittest.TestCase): 241 | def setUp(self): 242 | self.f = open('smalltestfile', 'rb') 243 | 244 | def tearDown(self): 245 | self.f.close() 246 | 247 | def testByteArrayEquivalence(self): 248 | a = MmapByteArray(self.f) 249 | self.assertEqual(a.bytelength, 8) 250 | self.assertEqual(len(a), 8) 251 | self.assertEqual(a[0], 0x01) 252 | self.assertEqual(a[1], 0x23) 253 | self.assertEqual(a[7], 0xef) 254 | self.assertEqual(a[0:1], bytearray([1])) 255 | self.assertEqual(a[:], bytearray([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])) 256 | self.assertEqual(a[2:4], bytearray([0x45, 0x67])) 257 | 258 | def testWithLength(self): 259 | a = MmapByteArray(self.f, 3) 260 | self.assertEqual(a[0], 0x01) 261 | self.assertEqual(len(a), 3) 262 | 263 | def testWithOffset(self): 264 | a = MmapByteArray(self.f, None, 5) 265 | self.assertEqual(len(a), 3) 266 | self.assertEqual(a[0], 0xab) 267 | 268 | def testWithLengthAndOffset(self): 269 | a = MmapByteArray(self.f, 3, 3) 270 | self.assertEqual(len(a), 3) 271 | self.assertEqual(a[0], 0x67) 272 | self.assertEqual(a[:], bytearray([0x67, 0x89, 0xab])) 273 | 274 | 275 | class Comparisons(unittest.TestCase): 276 | def testUnorderable(self): 277 | a = Bits(5) 278 | b = Bits(5) 279 | self.assertRaises(TypeError, a.__lt__, b) 280 | self.assertRaises(TypeError, a.__gt__, b) 281 | self.assertRaises(TypeError, a.__le__, b) 282 | self.assertRaises(TypeError, a.__ge__, b) 283 | 284 | 285 | class Subclassing(unittest.TestCase): 286 | 287 | def testIsInstance(self): 288 | class SubBits(bitstring.Bits): pass 289 | a = SubBits() 290 | self.assertTrue(isinstance(a, SubBits)) 291 | 292 | def testClassType(self): 293 | class SubBits(bitstring.Bits): pass 294 | self.assertEqual(SubBits().__class__, SubBits) 295 | -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test_bitstore.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import sys 5 | sys.path.insert(0, '..') 6 | from bitstring import ByteStore, ConstByteStore, equal, offsetcopy 7 | 8 | 9 | class OffsetCopy(unittest.TestCase): 10 | def testStraightCopy(self): 11 | s = ByteStore(bytearray([10, 5, 1]), 24, 0) 12 | t = offsetcopy(s, 0) 13 | self.assertEqual(t._rawarray, bytearray([10, 5, 1])) 14 | 15 | def testOffsetIncrease(self): 16 | s = ByteStore(bytearray([1, 1, 1]), 24, 0) 17 | t = offsetcopy(s, 4) 18 | self.assertEqual(t.bitlength, 24) 19 | self.assertEqual(t.offset, 4) 20 | self.assertEqual(t._rawarray, bytearray([0, 16, 16, 16])) 21 | 22 | 23 | class Equals(unittest.TestCase): 24 | 25 | def testBothSingleByte(self): 26 | s = ByteStore(bytearray([128]), 3, 0) 27 | t = ByteStore(bytearray([64]), 3, 1) 28 | u = ByteStore(bytearray([32]), 3, 2) 29 | self.assertTrue(equal(s, t)) 30 | self.assertTrue(equal(s, u)) 31 | self.assertTrue(equal(u, t)) 32 | 33 | def testOneSingleByte(self): 34 | s = ByteStore(bytearray([1, 0]), 2, 7) 35 | t = ByteStore(bytearray([64]), 2, 1) 36 | self.assertTrue(equal(s, t)) 37 | self.assertTrue(equal(t, s)) -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test_bitstring.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Module-level unit tests. 4 | """ 5 | 6 | import unittest 7 | import sys 8 | sys.path.insert(0, '..') 9 | import bitstring 10 | import copy 11 | 12 | 13 | class ModuleData(unittest.TestCase): 14 | def testVersion(self): 15 | self.assertEqual(bitstring.__version__, '3.0.2') 16 | 17 | def testAll(self): 18 | exported = ['ConstBitArray', 'ConstBitStream', 'BitStream', 'BitArray', 19 | 'Bits', 'BitString', 'pack', 'Error', 'ReadError', 20 | 'InterpretError', 'ByteAlignError', 'CreationError', 'bytealigned'] 21 | self.assertEqual(set(bitstring.__all__), set(exported)) 22 | 23 | def testReverseDict(self): 24 | d = bitstring.BYTE_REVERSAL_DICT 25 | for i in range(256): 26 | a = bitstring.Bits(uint=i, length=8) 27 | b = d[i] 28 | self.assertEqual(a.bin[::-1], bitstring.Bits(bytes=b).bin) 29 | 30 | def testAliases(self): 31 | self.assertTrue(bitstring.Bits is bitstring.ConstBitArray) 32 | self.assertTrue(bitstring.BitStream is bitstring.BitString) 33 | 34 | 35 | class MemoryUsage(unittest.TestCase): 36 | def testBaselineMemory(self): 37 | try: 38 | import pympler.asizeof.asizeof as size 39 | except ImportError: 40 | return 41 | # These values might be platform dependent, so don't fret too much. 42 | self.assertEqual(size(bitstring.ConstBitStream([0])), 64) 43 | self.assertEqual(size(bitstring.Bits([0])), 64) 44 | self.assertEqual(size(bitstring.BitStream([0])), 64) 45 | self.assertEqual(size(bitstring.BitArray([0])), 64) 46 | from bitstring.bitstore import ByteStore 47 | self.assertEqual(size(ByteStore(bytearray())), 100) 48 | 49 | 50 | class Copy(unittest.TestCase): 51 | def testConstBitArrayCopy(self): 52 | import copy 53 | cba = bitstring.Bits(100) 54 | cba_copy = copy.copy(cba) 55 | self.assertTrue(cba is cba_copy) 56 | 57 | def testBitArrayCopy(self): 58 | ba = bitstring.BitArray(100) 59 | ba_copy = copy.copy(ba) 60 | self.assertFalse(ba is ba_copy) 61 | self.assertFalse(ba._datastore is ba_copy._datastore) 62 | self.assertTrue(ba == ba_copy) 63 | 64 | def testConstBitStreamCopy(self): 65 | cbs = bitstring.ConstBitStream(100) 66 | cbs.pos = 50 67 | cbs_copy = copy.copy(cbs) 68 | self.assertEqual(cbs_copy.pos, 0) 69 | self.assertTrue(cbs._datastore is cbs_copy._datastore) 70 | self.assertTrue(cbs == cbs_copy) 71 | 72 | def testBitStreamCopy(self): 73 | bs = bitstring.BitStream(100) 74 | bs.pos = 50 75 | bs_copy = copy.copy(bs) 76 | self.assertEqual(bs_copy.pos, 0) 77 | self.assertFalse(bs._datastore is bs_copy._datastore) 78 | self.assertTrue(bs == bs_copy) 79 | 80 | 81 | class Interning(unittest.TestCase): 82 | def testBits(self): 83 | a = bitstring.Bits('0xf') 84 | b = bitstring.Bits('0xf') 85 | self.assertTrue(a is b) 86 | c = bitstring.Bits('0b1111') 87 | self.assertFalse(a is c) 88 | 89 | def testCBS(self): 90 | a = bitstring.ConstBitStream('0b11000') 91 | b = bitstring.ConstBitStream('0b11000') 92 | self.assertFalse(a is b) 93 | # self.assertTrue(a._datastore is b._datastore) 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /bitstring-3.0.2/test/test_constbitstream.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import sys 5 | sys.path.insert(0, '..') 6 | import bitstring 7 | from bitstring import ConstBitStream as CBS 8 | 9 | class All(unittest.TestCase): 10 | def testFromFile(self): 11 | s = CBS(filename='test.m1v') 12 | self.assertEqual(s[0:32].hex, '000001b3') 13 | self.assertEqual(s.read(8 * 4).hex, '000001b3') 14 | width = s.read(12).uint 15 | height = s.read(12).uint 16 | self.assertEqual((width, height), (352, 288)) 17 | 18 | 19 | class InterleavedExpGolomb(unittest.TestCase): 20 | def testReading(self): 21 | s = CBS(uie=333) 22 | a = s.read('uie') 23 | self.assertEqual(a, 333) 24 | s = CBS('uie=12, sie=-9, sie=9, uie=1000000') 25 | u = s.unpack('uie, 2*sie, uie') 26 | self.assertEqual(u, [12, -9, 9, 1000000]) 27 | 28 | def testReadingErrors(self): 29 | s = CBS(10) 30 | self.assertRaises(bitstring.ReadError, s.read, 'uie') 31 | self.assertEqual(s.pos, 0) 32 | self.assertRaises(bitstring.ReadError, s.read, 'sie') 33 | self.assertEqual(s.pos, 0) 34 | 35 | 36 | class ReadTo(unittest.TestCase): 37 | def testByteAligned(self): 38 | a = CBS('0xaabb00aa00bb') 39 | b = a.readto('0x00', bytealigned=True) 40 | self.assertEqual(b, '0xaabb00') 41 | self.assertEqual(a.bytepos, 3) 42 | b = a.readto('0xaa', bytealigned=True) 43 | self.assertEqual(b, '0xaa') 44 | self.assertRaises(bitstring.ReadError, a.readto, '0xcc', bytealigned=True) 45 | 46 | def testNotAligned(self): 47 | a = CBS('0b00111001001010011011') 48 | a.pos = 1 49 | self.assertEqual(a.readto('0b00'), '0b011100') 50 | self.assertEqual(a.readto('0b110'), '0b10010100110') 51 | self.assertRaises(ValueError, a.readto, '') 52 | 53 | def testDisallowIntegers(self): 54 | a = CBS('0x0f') 55 | self.assertRaises(ValueError, a.readto, 4) 56 | 57 | def testReadingLines(self): 58 | s = b"This is a test\nof reading lines\nof text\n" 59 | b = CBS(bytes=s) 60 | n = bitstring.Bits(bytes=b'\n') 61 | self.assertEqual(b.readto(n).bytes, b'This is a test\n') 62 | self.assertEqual(b.readto(n).bytes, b'of reading lines\n') 63 | self.assertEqual(b.readto(n).bytes, b'of text\n') 64 | 65 | 66 | class Subclassing(unittest.TestCase): 67 | 68 | def testIsInstance(self): 69 | class SubBits(CBS): pass 70 | a = SubBits() 71 | self.assertTrue(isinstance(a, SubBits)) 72 | 73 | def testClassType(self): 74 | class SubBits(CBS): pass 75 | self.assertEqual(SubBits().__class__, SubBits) 76 | -------------------------------------------------------------------------------- /docs/A3967.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/docs/A3967.pdf -------------------------------------------------------------------------------- /docs/EasyDriver_v43.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/docs/EasyDriver_v43.pdf -------------------------------------------------------------------------------- /docs/matrix_method_rev_e.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/docs/matrix_method_rev_e.pdf -------------------------------------------------------------------------------- /docs/stepper_datasheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/docs/stepper_datasheet.pdf -------------------------------------------------------------------------------- /images/General_sch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/General_sch.jpg -------------------------------------------------------------------------------- /images/gui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/gui.jpg -------------------------------------------------------------------------------- /images/parts/ArduinoDuemilanove.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/parts/ArduinoDuemilanove.jpg -------------------------------------------------------------------------------- /images/parts/easy_driver.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/parts/easy_driver.jpg -------------------------------------------------------------------------------- /images/parts/laser_img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/parts/laser_img.jpg -------------------------------------------------------------------------------- /images/parts/pf35t_MED.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/parts/pf35t_MED.jpg -------------------------------------------------------------------------------- /images/parts/unipolar_stepper_sch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/parts/unipolar_stepper_sch.png -------------------------------------------------------------------------------- /images/photo_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/photo_1.jpg -------------------------------------------------------------------------------- /images/photo_1_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/photo_1_small.jpg -------------------------------------------------------------------------------- /images/photo_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/images/photo_2.jpg -------------------------------------------------------------------------------- /main/README.md: -------------------------------------------------------------------------------- 1 | Software to control the "Laser pointer" device 2 | ------------------------------------------------ 3 | 4 | Includes all the necessary software to control the mini-dobsonian mount with the laser pointer. 5 | 6 | ![Device](https://raw.github.com/juanrmn/Arduino-Telescope-Control/master/images/photo_1_small.jpg) 7 | 8 | 9 | Once the device has been programmed with the software included in arduino folder, you can run the python 10 | GUI with: 11 | 12 | ./laser_control_main.py 13 | 14 | 15 | ![GUI](https://raw.github.com/juanrmn/Arduino-Telescope-Control/master/images/gui.jpg) 16 | 17 | 18 | **Note that two reference objects are required for initial configuration in order to obtain the transformation 19 | matrix.** 20 | -------------------------------------------------------------------------------- /main/arduino/plaser/AxesLib.cpp: -------------------------------------------------------------------------------- 1 | // #include "WProgram.h" // Arduino < 1.0 2 | #include //Arduino >= 1.0 3 | #include "AxesLib.h" 4 | 5 | AxesLib::AxesLib(){} 6 | 7 | void AxesLib::setMotorsPins(int stPin_x, int stPin_y, int dirPin, int enable_x, int enable_y){ 8 | _stPin_x = stPin_x; 9 | _stPin_y = stPin_y; 10 | _dirPin = dirPin; 11 | _enable_x = enable_x; 12 | _enable_y = enable_y; 13 | 14 | pinMode(_stPin_x, OUTPUT); 15 | pinMode(_stPin_y, OUTPUT); 16 | pinMode(_dirPin, OUTPUT); 17 | pinMode(_enable_x, OUTPUT); 18 | pinMode(_enable_y, OUTPUT); 19 | } 20 | 21 | void AxesLib::setSensorsPins(int s0Pin_x, int s360Pin_x,int sbottomPin_y, int stopPin_y){ 22 | _s0Pin_x = s0Pin_x; //0º sensor (the limit on counter clockwise direction on X axis) 23 | _s360Pin_x = s360Pin_x; //360º sensor (he limit on clockwise direction on X axis..) 24 | _sbottomPin_y = sbottomPin_y; //0º sensor (Y axis..) 25 | _stopPin_y = stopPin_y; //90º sensor 26 | 27 | pinMode(_s0Pin_x, INPUT); 28 | pinMode(_s360Pin_x, INPUT); 29 | pinMode(_sbottomPin_y, INPUT); 30 | pinMode(_stopPin_y, INPUT); 31 | } 32 | 33 | void AxesLib::_enableMotors(){ 34 | digitalWrite(_enable_x, LOW); 35 | digitalWrite(_enable_y, LOW); 36 | } 37 | void AxesLib::_disableMotors(){ 38 | digitalWrite(_enable_x, HIGH); 39 | digitalWrite(_enable_y, HIGH); 40 | } 41 | 42 | void AxesLib::init(){ 43 | //Here we supposes that 10000 steps are much more than one revolution.. 44 | //(anyway, this number can be arbitrarily bigger) 45 | int MAX_STEPS = 10000; 46 | 47 | //We are on (0,0) 48 | _x = 0; 49 | _X = 0; 50 | _x_rev = false; 51 | _y = 0; 52 | _rx = 0.0; 53 | _ry = 0.0; 54 | 55 | _enableMotors(); 56 | 57 | //Go to the 360º limit 58 | _step(_stPin_x, true, MAX_STEPS, _s360Pin_x); 59 | //Once there, count steps until the beginnin.. 60 | _pv_x = _step(_stPin_x, false, MAX_STEPS, _s0Pin_x); 61 | _pgrad_x = (float) _pv_x/360; 62 | 63 | //The same procedure but for Y axis.. 64 | _step(_stPin_y, true, MAX_STEPS, _stopPin_y); 65 | _pv_y = _step(_stPin_y, false, MAX_STEPS, _sbottomPin_y); 66 | _pgrad_y = (float) _pv_y/90; 67 | 68 | _disableMotors(); 69 | 70 | //Maximum values 71 | _X = (360*_pgrad_x); 72 | _Y = (180*_pgrad_y); 73 | //Auxiliary values in case of Y > 90º 74 | _revx = 180*_pgrad_x; 75 | _topy = 90*_pgrad_y; 76 | } 77 | 78 | int AxesLib::_step(int axis, bool dir, int steps, int sensor, bool nodelay){ 79 | int aux_x; 80 | 81 | //here, false value means clockwise.. 82 | dir = !dir; 83 | 84 | //Because the device construction, the Y axis goes in opposite direction than X 85 | if(axis==_stPin_y) 86 | dir = !dir; 87 | digitalWrite(_dirPin,dir); 88 | 89 | if(nodelay == false) 90 | delay(50); 91 | for(int i=0;i 1100) 129 | facel -= 1000; 130 | 131 | while(Serial.available() > 0) 132 | comm[bytes_recv++] = Serial.read(); 133 | //Safeguard... 134 | if(bytes_recv>4) 135 | break; 136 | } 137 | _disableMotors(); 138 | 139 | if( dir ) _x -= steps; 140 | else _x += steps; 141 | if(_x<0) _x = 0; 142 | if(_x>_X) _x = _X; 143 | if( (dir==false && digitalRead(_s360Pin_x)==HIGH) || (dir && digitalRead(_s0Pin_x)==HIGH) ) 144 | return true; 145 | return false; 146 | } 147 | bool AxesLib::movy(bool dir){ 148 | int steps = 0; 149 | char comm[5]="nost"; 150 | int bytes_recv = 0; 151 | int facel = 7100; 152 | 153 | _enableMotors(); 154 | 155 | //false value means upwards 156 | digitalWrite(_dirPin,dir); 157 | 158 | while(strcmp(comm, "stop")!=0){ 159 | if( (dir && digitalRead(_stopPin_y)==LOW) || (dir==false && digitalRead(_sbottomPin_y)==LOW) ) 160 | break; 161 | 162 | digitalWrite(_stPin_y, HIGH); 163 | delayMicroseconds(facel); 164 | digitalWrite(_stPin_y, LOW); 165 | delayMicroseconds(facel); 166 | 167 | steps++; 168 | 169 | if(steps % 50 == 0 && facel > 1100) 170 | facel -= 1000; 171 | 172 | while(Serial.available() > 0) 173 | comm[bytes_recv++] = Serial.read(); 174 | //Safeguard... 175 | if(bytes_recv>4) 176 | break; 177 | } 178 | _disableMotors(); 179 | 180 | if( dir ) _y += steps; 181 | else _y -= steps; 182 | if(_y<0) _y = 0; 183 | if(_y>_Y) _y = _Y; 184 | if( (dir && digitalRead(_stopPin_y)==LOW) || (dir==false && digitalRead(_sbottomPin_y)==LOW) ) 185 | return true; 186 | return false; 187 | } 188 | 189 | float AxesLib::getX(){ 190 | float degx; 191 | 192 | if(_x_rev==false) degx = (float) _x/_pgrad_x; 193 | else{ 194 | if(_x>=_revx) 195 | degx = (float) (_x-_revx)/_pgrad_x; 196 | else 197 | degx = (float) (_x+_revx)/_pgrad_x; 198 | } 199 | _rx = _deg2rad(360.0 - degx); 200 | return _rx; 201 | } 202 | 203 | float AxesLib::getY(){ 204 | if(_x_rev==false) 205 | _ry = _deg2rad((float) _y/_pgrad_y); 206 | else 207 | _ry = _deg2rad((float) (_topy+(_topy-_y))/_pgrad_y); 208 | return _ry; 209 | } 210 | 211 | int AxesLib::getPX(){ 212 | return _pv_x; 213 | } 214 | 215 | int AxesLib::getPx(){ 216 | return _x; 217 | } 218 | 219 | int AxesLib::getPY(){ 220 | return _pv_y*4; 221 | } 222 | 223 | int AxesLib::getPy(){ 224 | return _y; 225 | } 226 | 227 | int AxesLib::_rad2deg(float rad){ 228 | return lrint( (float) (rad * 180.0)/M_PI ); 229 | } 230 | 231 | float AxesLib::_deg2rad(float deg){ 232 | return (float) (deg * M_PI)/ 180; 233 | } 234 | 235 | void AxesLib::goToRads(float rx, float ry){ 236 | float degsH = (360.0 - _rad2deg(rx)); 237 | if(degsH >= 360.0) 238 | degsH = degsH - 360.0; 239 | 240 | _moveTo((float) degsH*_pgrad_x, (float) _rad2deg(ry)*_pgrad_y); 241 | } 242 | 243 | void AxesLib::_moveTo(int x, int y, char* method){ 244 | _enableMotors(); 245 | 246 | if(x<0) x = _X - fabs(x); 247 | if(0<0) y = 0; 248 | if(x>_X) x = _X; 249 | if(y>_Y) y = _Y; 250 | 251 | //Lets see if X axis has been "reverted" previously.. 252 | if(y>_topy && _x_rev==false){ 253 | _x_rev = true; 254 | y = _topy - (y-_topy); 255 | if(x>=_revx) x = x-_revx; 256 | else x = x+_revx; 257 | }else if(y>_topy && _x_rev){ 258 | y = _topy - (y-_topy); 259 | if(x>=_revx) x = x-_revx; 260 | else x = x+_revx; 261 | }else if(_x_rev) 262 | _x_rev = false; 263 | 264 | if(strcmp(method, "DDA")==0) 265 | _moveDDA(x, y); 266 | else if(strcmp(method, "XY")==0) 267 | _moveXY(x, y); 268 | 269 | _disableMotors(); 270 | } 271 | 272 | void AxesLib::_moveXY(int x, int y, bool nodelay){ 273 | if(x>_x) 274 | _x += _step(_stPin_x, (x>_x), (x-_x), _s360Pin_x, nodelay); 275 | else 276 | _x -= _step(_stPin_x, (x>_x), (_x-x), _s0Pin_x, nodelay); 277 | 278 | if(y>_y) 279 | _y += _step(_stPin_y, (y>_y), (y-_y), _stopPin_y, nodelay); 280 | else 281 | _y -= _step(_stPin_y, (y>_y), (_y-y), _sbottomPin_y, nodelay); 282 | } 283 | 284 | void AxesLib::_moveDDA(int x, int y){ 285 | int dx, dy, steps; 286 | float x_inc, y_inc, x_, y_; 287 | 288 | dx = x-_x; 289 | dy = y-_y; 290 | 291 | if(fabs(dx)>fabs(dy)) 292 | steps = fabs(dx); 293 | else 294 | steps = fabs(dy); 295 | 296 | x_inc = (float) dx/steps; 297 | y_inc = (float) dy/steps; 298 | x_ = _x; 299 | y_ = _y; 300 | 301 | for(int i=1; i 5 | #include 6 | 7 | /** 8 | * \brief Class that manages movements and the laser of the device 9 | * 10 | * Uses the stepper motors and sensors to positioning the device at a given horizontal coordinates, 11 | * within a range of 360º degrees in horizontal, and 180º on vertical. 12 | * To the movements uses the DDA algorithm (Digital Differential Algorithm). 13 | */ 14 | class AxesLib{ 15 | private: 16 | /** 17 | * Pins to control the stepper motors 18 | */ 19 | int _stPin_x, _stPin_y, _dirPin, _enable_x, _enable_y; 20 | 21 | /** 22 | * Steps per degree on each axis 23 | */ 24 | float _pgrad_x, _pgrad_y; 25 | 26 | /** 27 | * Current position of each axis (in radians) 28 | */ 29 | float _rx, _ry; 30 | 31 | /** 32 | * Auxiliary variables, maximum theoric values, and steps per revolution respectively 33 | */ 34 | int _x, _y, _X, _Y, _pv_x, _pv_y; 35 | 36 | /** 37 | * Theoric central values of each axis 38 | */ 39 | int _revx, _topy; 40 | 41 | /** 42 | * Indicates if X axis has been "reverted" (Y is between 90º and 180º) 43 | */ 44 | bool _x_rev; 45 | 46 | /** 47 | * Sensor pins 48 | */ 49 | int _s0Pin_x, _s360Pin_x, _sbottomPin_y, _stopPin_y; 50 | 51 | /** 52 | * Transforms radian to degrees 53 | * 54 | * \param rad Radians 55 | * \return degrees 56 | */ 57 | int _rad2deg(float rad); 58 | 59 | /** 60 | * Degrees to radians 61 | * 62 | * \param deg Degrees 63 | * \return radians 64 | */ 65 | float _deg2rad(float deg); 66 | 67 | /** 68 | * Moves one of the motors a given number of steps 69 | * 70 | * \param axis Pin of the motor to move 71 | * \param dir Direction: True means clockwise direction on X, and upwards on Y 72 | * \param steps Number of steps (if limit sensor is not reached) 73 | * \param sensor Pin of the sensor that can be reached towards that direction 74 | * \param nodelay Skip the initial delay (useful to DDA algorithm) 75 | * \return Number of steps (distinct of the steps parameter if the sensor has been reached) 76 | */ 77 | int _step(int axis, bool dir, int steps, int sensor, bool nodelay=false); 78 | 79 | /** 80 | * Enables the motors power supply 81 | */ 82 | void _enableMotors(); 83 | 84 | /** 85 | * Disables the motors power supply 86 | */ 87 | void _disableMotors(); 88 | 89 | /** 90 | * Moves the device to the given position 91 | * 92 | * \param x Number of steps from 0 to the desired position on X axis 93 | * \param y Number of steps from 0 to the desired position on Y axis 94 | * \param method Algorithm selection: DDA or XY (first X axis, then Y), by default is DDA 95 | */ 96 | void _moveTo(int x, int y, char* method = "DDA"); 97 | 98 | /** 99 | * Moves the device to the given position, first X axis and then Y axis 100 | * 101 | * \param x Number of steps from 0 to the desired position on X axis 102 | * \param y Number of steps from 0 to the desired position on Y axis 103 | * \param nodelay Omits the delay on changes of axis or direction 104 | */ 105 | void _moveXY(int x, int y, bool nodelay=false); 106 | 107 | /** 108 | * Moves the device to the given position using DDA algorithm 109 | * 110 | * \param x Number of steps from 0 to the desired position on X axis 111 | * \param y Number of steps from 0 to the desired position on Y axis 112 | */ 113 | void _moveDDA(int x, int y); 114 | 115 | public: 116 | /** 117 | * Class constructor 118 | */ 119 | AxesLib(); 120 | 121 | /** 122 | * Initializes the device 123 | * 124 | * Along the process obtains the number of steps on each axis, and calculates the steps 125 | * per degree for positioning 126 | */ 127 | void init(); 128 | 129 | /** 130 | * Returns current position on X axis 131 | * 132 | * \return X position as radians 133 | */ 134 | float getX(); 135 | 136 | /** 137 | * Returns current position on Y axis 138 | * 139 | * \return Y position as radians 140 | */ 141 | float getY(); 142 | 143 | /** 144 | * Return the number of steps per revolution of the X axis 145 | * 146 | * \return Steps per revolution 147 | */ 148 | int getPX(); 149 | 150 | /** 151 | * Number of steps from 0º to current position on X axis 152 | * 153 | * \return Current position on X 154 | */ 155 | int getPx(); 156 | 157 | /** 158 | * Return the number of steps per revolution of the Y axis 159 | * 160 | * \return Steps per revolution 161 | */ 162 | int getPY(); 163 | 164 | /** 165 | * Number of steps from 0º to current position on Y axis 166 | * 167 | * \return Current position on Y 168 | */ 169 | int getPy(); 170 | 171 | /** 172 | * Sets the pins to control the stepper motors 173 | * 174 | * \param stPin_x Pin to move the X axis 175 | * \param stPin_y Pin to move the Y axis 176 | * \param dirPin Direction: True means clockwise direction on X axis, and downwards on Y 177 | * \param enable_x Turn On/Off power supply on X axis motor 178 | * \param enable_y Turn On/Off power supply on Y axis motor 179 | */ 180 | void setMotorsPins(int stPin_x, int stPin_y, int dirPin, int enable_x, int enable_y); 181 | 182 | /** 183 | * Sets the pins connected to the sensors 184 | * 185 | * \param s0Pin_x Pin for the 0º limit sensor on X axis 186 | * \param s360Pin_x Pir for the 360º limit sensor on X axis 187 | * \param sbottomPin_y Pin for the 0º limit sensor on Y axis 188 | * \param stopPin_y Sensor for the 90º limit sensor on Y axis 189 | */ 190 | void setSensorsPins(int s0Pin_x, int s360Pin_x, int sbottomPin_y, int stopPin_y); 191 | 192 | /** 193 | * Points the device towards the given coordinates 194 | * 195 | * \param rx Radians for the X axis, on range of 0 - 2*Pi 196 | * \param ry Radians for the Y axis: on range of 0 - Pi 197 | */ 198 | void goToRads(float rx, float ry); 199 | 200 | /** 201 | * Accelerated movement for X axis 202 | * 203 | * The movement stops when a 'stop' command is received by the serial port 204 | * 205 | * \param dir Direction: True means clockwise direction 206 | * \return Returns true in case of reaches a limit sensor 207 | */ 208 | bool movx(bool dir); 209 | 210 | /** 211 | * Accelerated movement for Y axis 212 | * 213 | * The movement stops when a 'stop' command is received by the serial port 214 | * 215 | * \param dir Direction: True means upwards 216 | * \return Returns true in case of reaches a limit sensor 217 | */ 218 | bool movy(bool dir); 219 | }; 220 | #endif 221 | -------------------------------------------------------------------------------- /main/arduino/plaser/CoordsLib.cpp: -------------------------------------------------------------------------------- 1 | // #include "WProgram.h" // Arduino < 1.0 2 | #include //Arduino >= 1.0 3 | #include "CoordsLib.h" 4 | 5 | CoordsLib::CoordsLib(){ 6 | _t0 = 0; 7 | _k = 1.002737908; // Constant.. Relationship between the solar time (M) and the sidereal time (S): (S = M * 1.002737908) 8 | _isSetR1 = false; 9 | _isSetR2 = false; 10 | _isSetR3 = false; 11 | } 12 | 13 | /* 14 | * Calculates the inverse of the m[3x3] matrix and returns it in the second parameter. 15 | */ 16 | void CoordsLib::_inv(float m[3][3], float res[3][3]){ 17 | float idet; 18 | 19 | //Inverse of the determinant 20 | idet = 1/( 21 | (m[0][0]*m[1][1]*m[2][2]) + (m[0][1]*m[1][2]*m[2][0]) + (m[0][2]*m[1][0]*m[2][1]) 22 | - (m[0][2]*m[1][1]*m[2][0]) - (m[0][1]*m[1][0]*m[2][2]) - (m[0][0]*m[1][2]*m[2][1]) 23 | ); 24 | 25 | res[0][0] = ((m[1][1]*m[2][2]) - (m[2][1]*m[1][2]))*idet; 26 | res[0][1] = ((m[2][1]*m[0][2]) - (m[0][1]*m[2][2]))*idet; 27 | res[0][2] = ((m[0][1]*m[1][2]) - (m[1][1]*m[0][2]))*idet; 28 | 29 | res[1][0] = ((m[1][2]*m[2][0]) - (m[2][2]*m[1][0]))*idet; 30 | res[1][1] = ((m[2][2]*m[0][0]) - (m[0][2]*m[2][0]))*idet; 31 | res[1][2] = ((m[0][2]*m[1][0]) - (m[1][2]*m[0][0]))*idet; 32 | 33 | res[2][0] = ((m[1][0]*m[2][1]) - (m[2][0]*m[1][1]))*idet; 34 | res[2][1] = ((m[2][0]*m[0][1]) - (m[0][0]*m[2][1]))*idet; 35 | res[2][2] = ((m[0][0]*m[1][1]) - (m[1][0]*m[0][1]))*idet; 36 | } 37 | 38 | /* 39 | * Multiplies two matrices, m1[3x3] and m2[3x3], and returns the result in 40 | * the third parameter. 41 | */ 42 | void CoordsLib::_m_prod(float m1[3][3], float m2[3][3], float res[3][3]){ 43 | for(int i=0; i<3; i++) 44 | for(int j=0; j<3; j++){ 45 | res[i][j] = 0.0; 46 | for(int k=0; k<3; k++) //multiplying row by column 47 | res[i][j] += m1[i][k] * m2[k][j]; 48 | } 49 | } 50 | 51 | /* 52 | * Calculates the Vector cosines (EVC) from the equatorial coordinates (ar, dec, t). 53 | */ 54 | void CoordsLib::_setEVC(float ar, float dec, float t, float* EVC){ 55 | EVC[0] = cos(dec)*cos(ar - _k*(t-_t0)); 56 | EVC[1] = cos(dec)*sin(ar - _k*(t-_t0)); 57 | EVC[2] = sin(dec); 58 | } 59 | 60 | /* 61 | * Calculates the Vector cosines (HVC) from the horizontal coordinates (ac, alt). 62 | */ 63 | void CoordsLib::_setHVC(float ac, float alt, float* HVC){ 64 | HVC[0] = cos(alt)*cos(ac); 65 | HVC[1] = cos(alt)*sin(ac); 66 | HVC[2] = sin(alt); 67 | } 68 | 69 | /* 70 | * Sets the initial observation time. 71 | */ 72 | void CoordsLib::setTime(float t0){ 73 | _t0 = t0; 74 | } 75 | 76 | /* 77 | * Sets the first reference object. 78 | * If all the reference objects have been established, calls the function that calculates T and iT. 79 | */ 80 | void CoordsLib::setRef_1(float ar, float dec, float t, float ac, float alt){ 81 | _setEVC(ar, dec, t, _LMN1); 82 | _setHVC(ac, alt, _lmn1); 83 | _isSetR1 = true; 84 | _isSetR3 = false; 85 | 86 | if(_isSetR1 && _isSetR2 && _isSetR3) 87 | _setT(); 88 | } 89 | 90 | /* 91 | * Sets the second reference object. 92 | * If all the reference objects have been established, calls the function that calculates T and iT. 93 | */ 94 | void CoordsLib::setRef_2(float ar, float dec, float t, float ac, float alt){ 95 | _setEVC(ar, dec, t, _LMN2); 96 | _setHVC(ac, alt, _lmn2); 97 | _isSetR2 = true; 98 | _isSetR3 = false; 99 | 100 | if(_isSetR1 && _isSetR2 && _isSetR3) 101 | _setT(); 102 | } 103 | 104 | /* 105 | * Sets the third reference object. 106 | * If all the reference objects have been established, calls the function that calculates T and iT. 107 | */ 108 | void CoordsLib::setRef_3(float ar, float dec, float t, float ac, float alt){ 109 | _setEVC(ar, dec, t, _LMN3); 110 | _setHVC(ac, alt, _lmn3); 111 | _isSetR3 = true; 112 | 113 | if(_isSetR1 && _isSetR2 && _isSetR3) 114 | _setT(); 115 | } 116 | 117 | /** 118 | * Indicates if the three reference objects have been established. 119 | */ 120 | bool CoordsLib::isConfigured(){ 121 | return (_isSetR1 && _isSetR2 && _isSetR3); 122 | } 123 | 124 | /* 125 | * Third reference object calculated from the cross product of the two first ones. 126 | * Then calls the function that calculates T and iT. 127 | */ 128 | void CoordsLib::autoRef_3(){ 129 | float sqrt1, sqrt2; 130 | 131 | if(_isSetR1 && _isSetR2){ 132 | sqrt1 = (1/( sqrt( pow(( (_lmn1[1]*_lmn2[2]) - (_lmn1[2]*_lmn2[1])),2) + 133 | pow(( (_lmn1[2]*_lmn2[0]) - (_lmn1[0]*_lmn2[2])),2) + 134 | pow(( (_lmn1[0]*_lmn2[1]) - (_lmn1[1]*_lmn2[0])),2)) 135 | )); 136 | _lmn3[0] = sqrt1 * ( (_lmn1[1]*_lmn2[2]) - (_lmn1[2]*_lmn2[1]) ); 137 | _lmn3[1] = sqrt1 * ( (_lmn1[2]*_lmn2[0]) - (_lmn1[0]*_lmn2[2]) ); 138 | _lmn3[2] = sqrt1 * ( (_lmn1[0]*_lmn2[1]) - (_lmn1[1]*_lmn2[0]) ); 139 | 140 | sqrt2 = (1/( sqrt( pow(( (_LMN1[1]*_LMN2[2]) - (_LMN1[2]*_LMN2[1])),2) + 141 | pow(( (_LMN1[2]*_LMN2[0]) - (_LMN1[0]*_LMN2[2])),2) + 142 | pow(( (_LMN1[0]*_LMN2[1]) - (_LMN1[1]*_LMN2[0])),2)) 143 | )); 144 | _LMN3[0] = sqrt2 * ( (_LMN1[1]*_LMN2[2]) - (_LMN1[2]*_LMN2[1]) ); 145 | _LMN3[1] = sqrt2 * ( (_LMN1[2]*_LMN2[0]) - (_LMN1[0]*_LMN2[2]) ); 146 | _LMN3[2] = sqrt2 * ( (_LMN1[0]*_LMN2[1]) - (_LMN1[1]*_LMN2[0]) ); 147 | _isSetR3 = true; 148 | 149 | if(_isSetR1 && _isSetR2 && _isSetR3) 150 | _setT(); 151 | } 152 | } 153 | 154 | /* 155 | * Sets the transformation matrix and its inverse (T and iT, respectively). 156 | */ 157 | void CoordsLib::_setT(){ 158 | float subT1[3][3], subT2[3][3], aux[3][3]; 159 | 160 | subT1[0][0] = _lmn1[0]; subT1[0][1] = _lmn2[0]; subT1[0][2] = _lmn3[0]; 161 | subT1[1][0] = _lmn1[1]; subT1[1][1] = _lmn2[1]; subT1[1][2] = _lmn3[1]; 162 | subT1[2][0] = _lmn1[2]; subT1[2][1] = _lmn2[2]; subT1[2][2] = _lmn3[2]; 163 | 164 | subT2[0][0] = _LMN1[0]; subT2[0][1] = _LMN2[0]; subT2[0][2] = _LMN3[0]; 165 | subT2[1][0] = _LMN1[1]; subT2[1][1] = _LMN2[1]; subT2[1][2] = _LMN3[1]; 166 | subT2[2][0] = _LMN1[2]; subT2[2][1] = _LMN2[2]; subT2[2][2] = _LMN3[2]; 167 | 168 | _inv(subT2, aux); 169 | _m_prod(subT1, aux, _T); 170 | _inv(_T, _iT); 171 | } 172 | 173 | /* 174 | * Horizontal coordinates (ac, alt) obtained from equatorial ones and time (ar, dec, t). 175 | * 176 | * If the third reference object is not established, it calculates it by calling the 177 | * proper function. 178 | */ 179 | void CoordsLib::getHCoords(float ar, float dec, float t, float *ac, float *alt){ 180 | float HVC[3]; 181 | float EVC[3]; 182 | _setEVC(ar, dec, t, EVC); 183 | 184 | if(!_isSetR3) 185 | autoRef_3(); 186 | 187 | for(int i=0; i<3; i++) 188 | HVC[i] = 0.0; 189 | for(int i=0; i<3; i++) 190 | for(int j=0; j<3; j++) 191 | HVC[i] += _T[i][j] * EVC[j]; 192 | 193 | (*ac) = atan2(HVC[1], HVC[0]); 194 | (*alt) = asin(HVC[2]); 195 | } 196 | 197 | /* 198 | * Equatorial coordinates (ar, dec) obtained from horizontal ones and time (ac, alt, t). 199 | * 200 | * If the third reference object is not established, it calculates it by calling the 201 | * proper function. 202 | */ 203 | void CoordsLib::getECoords(float ac, float alt, float t, float *ar, float *dec){ 204 | float HVC[3]; 205 | float EVC[3]; 206 | _setHVC(ac, alt, HVC); 207 | 208 | if(!_isSetR3) 209 | autoRef_3(); 210 | 211 | for(int i=0; i<3; i++) 212 | EVC[i] = 0.0; 213 | for(int i=0; i<3; i++) 214 | for(int j=0; j<3; j++) 215 | EVC[i] += _iT[i][j] * HVC[j]; 216 | 217 | (*ar) = atan2(EVC[1], EVC[0]) + (_k*(t-_t0)); 218 | (*dec) = asin(EVC[2]); 219 | } 220 | -------------------------------------------------------------------------------- /main/arduino/plaser/CoordsLib.h: -------------------------------------------------------------------------------- 1 | #ifndef CoordsLib_h 2 | #define CoordsLib_h 3 | 4 | #include 5 | 6 | /** 7 | * \brief Library for coordinates transformations. Calculates the equivalent coordinates between both coordinate systems equatorial and horizontal. 8 | * 9 | * It's based on Toshimi Taki's matrix method for coordinates transformation: http://www.geocities.jp/toshimi_taki/matrix/matrix.htm 10 | * Contains the necessary methods for setting the initial time, the reference objects, the transformation matrix, and to 11 | * calculate the equivalent vectors between both coordinate systems. 12 | */ 13 | class CoordsLib{ 14 | private: 15 | 16 | /** 17 | * Constant of multiplication for the solar and sidereal time relation. 18 | */ 19 | float _k; 20 | 21 | /** 22 | * Initial timestamp for the observations. 23 | */ 24 | float _t0; 25 | 26 | /** 27 | * Indicators for definition of the three reference objects. 28 | */ 29 | bool _isSetR1, _isSetR2, _isSetR3; 30 | 31 | /** 32 | * Auxiliary matrices. 33 | */ 34 | float _lmn1[3], _LMN1[3], _lmn2[3], _LMN2[3], _lmn3[3], _LMN3[3]; 35 | 36 | /** 37 | * Transformation matrix. Transform vectors from equatorial to horizontal system. 38 | */ 39 | float _T[3][3]; 40 | 41 | /** 42 | * Inverse transformation matrix. Transform vectors from horizontal to equatorial system. 43 | */ 44 | float _iT[3][3]; 45 | 46 | /** 47 | * If the three reference objects have been defined, it calculates the transformation matrix from them. 48 | */ 49 | void _setT(); 50 | 51 | /** 52 | * Obtains a vector in polar notation from the equatorial coordinates and the observation time. 53 | * 54 | * \param ar Right ascension. 55 | * \param dec Declination. 56 | * \param t Timestamp of the observation. 57 | * \param *EVC Pointer to array: Returns the three dimensional vector in polar notation. 58 | */ 59 | void _setEVC(float ar, float dec, float t, float* EVC); 60 | 61 | /** 62 | * Obtains a vector in polar notation from the horizontal coordinates and observation time. 63 | * 64 | * \param ac Azimuth. 65 | * \param alt Altitude. 66 | * \param t Timestamp of the observation. 67 | * \param *HVC Pointer to array: Returns the three dimensional vector in polar notation. 68 | */ 69 | void _setHVC(float ac, float alt, float* HVC); 70 | 71 | /** 72 | * Calculates the 3x3 inverse matrix. 73 | * 74 | * \param m[3][3] Input matrix. 75 | * \param res[3][3] Pointer to array: Returns the inverse matrix. 76 | */ 77 | void _inv(float m[3][3], float res[3][3]); 78 | 79 | /** 80 | * Calculates the product of 3x3 matrices. 81 | * 82 | * \param m1[3][3] Input matrix 1. 83 | * \param m2[3][3] Input matrix 2. 84 | * \param res[3][3] Pointer to array: Returns the result matrix. 85 | */ 86 | void _m_prod(float m1[3][3], float m2[3][3], float res[3][3]); 87 | 88 | public: 89 | 90 | /** 91 | * Class constructor. 92 | */ 93 | CoordsLib(); 94 | 95 | /** 96 | * Sets the initial time. 97 | * 98 | * This parameter is used in order to consider time passing on horizontal coordinates system. 99 | * 100 | * \param t0 Unix Timestamp of the initial observation time. 101 | */ 102 | void setTime(float t0); 103 | 104 | /** 105 | * Sets the first reference object from the coordinates in both coordinates systems for 106 | * that object. 107 | * 108 | * \param ar Right Ascension (equatorial coordinates). 109 | * \param dec Declination (equatorial coordinates). 110 | * \param t Unix Timestamp of the Observation. 111 | * \param ac Azimuth (horizontal coordinates). 112 | * \param alt Altitude (horizontal coordinates). 113 | */ 114 | void setRef_1(float ar, float dec, float t, float ac, float alt); 115 | 116 | /** 117 | * Sets the second reference object from the coordinates in both coordinates systems for 118 | * that object. 119 | * 120 | * \param ar Right Ascension (equatorial coordinates). 121 | * \param dec Declination (equatorial coordinates). 122 | * \param t Unix Timestamp of the Observation. 123 | * \param ac Azimuth (horizontal coordinates). 124 | * \param alt Altitude (horizontal coordinates). 125 | */ 126 | void setRef_2(float ar, float dec, float t, float ac, float alt); 127 | 128 | /** 129 | * Sets the third reference object from the coordinates in both coordinates systems for 130 | * that object. 131 | * 132 | * \param ar Right Ascension (equatorial coordinates). 133 | * \param dec Declination (equatorial coordinates). 134 | * \param t Unix Timestamp of the Observation. 135 | * \param ac Azimuth (horizontal coordinates). 136 | * \param alt Altitude (horizontal coordinates). 137 | */ 138 | void setRef_3(float ar, float dec, float t, float ac, float alt); 139 | 140 | /** 141 | * Indicates if the three reference objects has been calculated. 142 | * 143 | * \return Boolean. 144 | */ 145 | bool isConfigured(); 146 | 147 | /** 148 | * Third reference object calculated from the two others ones. 149 | * 150 | * Calculates the cross product of the two first reference objects in both coordinates systems, in order 151 | * to obtain the third one. 152 | * These two first objects must have 90º from each other, approximately (from 60º to 120º is enough to obtain 153 | * goods results). 154 | */ 155 | void autoRef_3(); 156 | 157 | /** 158 | * Horizontal coordinates calculated from the equatorial ones and time. 159 | * 160 | * \param ar Right Ascension (equatorial coordinates). 161 | * \param dec Declination (equatorial coordinates) 162 | * \param t Unix Timestamp of the Observation. 163 | * \param *ac Pointer to float: Returns the azimuth (horizontal coordiantes). 164 | * \param *alt Pointer to float: Returns the altitude (horizontal coordinates). 165 | */ 166 | void getHCoords(float ar, float dec, float t, float *ac, float *alt); 167 | 168 | /** 169 | * Equatorial coordinates calculated from the horizontal ones and time. 170 | * 171 | * \param ac Azimuth (horizontal coordinates). 172 | * \param alt Altitude (horizontal coordinates). 173 | * \param t Unix Timestamp of the Observation. 174 | * \param *ar Pointer to float: Returns the right ascension (equatorial coordinates). 175 | * \param *dec Pointer to float: Returns the declination (equatorial coordinates). 176 | */ 177 | void getECoords(float ac, float alt, float t, float *ar, float *dec); 178 | }; 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /main/arduino/plaser/plaser.pde: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "CoordsLib.h" 5 | #include "AxesLib.h" 6 | 7 | 8 | /** 9 | * Output pin for the STEP on X axis of the stepper controller (horizontal) 10 | */ 11 | int stepperPin1 = 3; 12 | 13 | /** 14 | * Output pin for the STEP on Y axis of the stepper controller (vertical) 15 | */ 16 | int stepperPin2 = 4; 17 | 18 | /** 19 | * Output pin for the rotate direction of both axes 20 | * (DIR pin of the stepper controllers) 21 | */ 22 | int steppersDir = 2; 23 | 24 | /** 25 | * Enable the engine power supply of the X axis 26 | * (ENABLE pin of the controller) 27 | */ 28 | int enableStepper1 = 9; 29 | 30 | /** 31 | * Enables the engine power supply of the Y axis 32 | * (ENABLE pin of the controller) 33 | */ 34 | int enableStepper2 = 10; 35 | 36 | /** 37 | * Input pin for the end limit sensor of the X axis (360º) 38 | */ 39 | int sensor360H = 5; 40 | 41 | /** 42 | * Input pin for the init limit sensor of the X axis (0º) 43 | */ 44 | int sensor0H = 11; 45 | 46 | /** 47 | * Input pin for the bottom limit sensor of the Y axis (0º) 48 | */ 49 | int sensorBottomV = 6; 50 | 51 | /** 52 | * Input pin for the top limit sensor of the Y axis (90º) 53 | */ 54 | int sensorTopV = 7; 55 | 56 | /** 57 | * Output pin to control the laser pointer 58 | */ 59 | int laserPin = 8; 60 | 61 | /** 62 | * Library for coordinates transformations 63 | */ 64 | CoordsLib Coords = CoordsLib(); 65 | 66 | /** 67 | * Library for control the device: stepper motors, sensors, current position.. 68 | */ 69 | AxesLib Axes = AxesLib(); 70 | 71 | /** 72 | * Initializes the serial port and sets pins to control the device 73 | */ 74 | void setup(){ 75 | Serial.begin(9600); 76 | Serial.println("init"); 77 | 78 | pinMode(laserPin, OUTPUT); 79 | laserOff(); 80 | 81 | Axes.setMotorsPins(stepperPin1, stepperPin2, steppersDir, enableStepper1, enableStepper2); 82 | Axes.setSensorsPins(sensor0H, sensor360H, sensorBottomV, sensorTopV); 83 | } 84 | 85 | /** 86 | * Turn the laser On 87 | */ 88 | void laserOn(){ 89 | digitalWrite(laserPin, LOW); 90 | } 91 | 92 | /** 93 | * Turn the laser Off 94 | */ 95 | void laserOff(){ 96 | digitalWrite(laserPin, HIGH); 97 | } 98 | 99 | /* 100 | * Get a float value from the serial port, and send the '_OK_' ack string. 101 | * The value must conains six decimals, in a string with 9 bytes, including sign and decimal dot. 102 | * Examples: '-0.036526', '+5.238388' 103 | * 104 | * \return float. 105 | */ 106 | float serialGetFloat(){ 107 | char bytes[9], sign; 108 | int nbytes = 0; 109 | float fex; 110 | bool recv = false; 111 | 112 | bytes[8] = '\0'; 113 | Serial.println("float"); 114 | while(!recv){ 115 | if (Serial.available() > 0) { 116 | sign = Serial.read();//Float with eight representation bytes (including dot and sign) 117 | while(nbytes < 8) 118 | if(Serial.available() > 0){ 119 | bytes[nbytes] = Serial.read(); 120 | nbytes++; 121 | } 122 | fex = strtod(bytes, NULL); 123 | if(sign=='-') 124 | fex = 0.0 - fex; 125 | recv = true; 126 | } 127 | } 128 | Serial.println("_OK_"); 129 | return fex; 130 | } 131 | 132 | /** 133 | * Main loop.. 134 | * 135 | * Obtains and executes the commands received by the serial port 136 | * 137 | * Avaliable commands: 138 | * 139 | *- 'init' () -> (float px, float py) Initializes the device and returns the steps by revolution obtained 140 | *- 'time' (float t) -> () Sets initial observation time 141 | *- 'set1' (float ar, float dec, float t, float ac, float alt) -> () Sets the first reference object 142 | *- 'set2' (float ar, float dec, float t, float ac, float alt) -> () Sets the second reference object 143 | *- 'set3' (float ar, float dec, float t, float ac, float alt) -> () Sets the third reference object (usually not used..) 144 | *- 'goto' (float ar, float dec, float t) -> (float ar, float dec)(float ac, float alt) Points the device towards the received equatorial coordinates 145 | *- 'move' () -> (float px, float py)(float ar, float dec)(float ac, float alt) Points the device towards the received horizontal coordinates 146 | *- 'movx' (char dir) -> (float ar, float dec)(float ac, float alt) Starts the accelerated horizontal movement towards the indicated direction 147 | *- 'movy' (char dir) Starts the accelerated vertical movement towards the indicated direction 148 | *- 'stop' () -> () Stops the movements initiated by movx or movy commands 149 | *- 'laon' () -> () Turn the laser On 150 | *- 'loff' () -> () Turn the laser Off 151 | */ 152 | void loop(){ 153 | float t0; 154 | float ar, dec, t; 155 | float ac, alt; 156 | char comm[5]; 157 | char dir; 158 | int bytes_recv = 0; 159 | bool mov_end; 160 | 161 | comm[4]='\0'; 162 | Serial.println("cmd"); 163 | while(bytes_recv < 4){ 164 | //Waiting for a command... 165 | if (Serial.available() > 0) 166 | comm[bytes_recv++] = Serial.read(); 167 | } 168 | 169 | //Obtaining the expected parameters of the command 170 | if(strcmp(comm, "set1")==0 || strcmp(comm, "set2")==0 || strcmp(comm, "set3")==0 || strcmp(comm, "goto")==0){ 171 | ar = serialGetFloat(); 172 | dec = serialGetFloat(); 173 | t = serialGetFloat(); 174 | } 175 | if(strcmp(comm, "move")==0){ 176 | ac = serialGetFloat(); 177 | alt = serialGetFloat(); 178 | t = serialGetFloat(); 179 | } 180 | 181 | //Executing command 182 | 183 | if(strcmp(comm, "time")==0){ 184 | t0 = serialGetFloat(); 185 | Coords.setTime(t0); 186 | Serial.println(); 187 | Serial.println("done_time"); 188 | }else if(strcmp(comm, "set1")==0){ 189 | Coords.setRef_1(ar, dec, t, Axes.getX(), Axes.getY()); 190 | Serial.println(); 191 | Serial.println("done_set1"); 192 | }else if(strcmp(comm, "set2")==0){ 193 | Coords.setRef_2(ar, dec, t, Axes.getX(), Axes.getY()); 194 | Serial.println(); 195 | Serial.println("done_set2"); 196 | }else if(strcmp(comm, "set3")==0){ 197 | Coords.setRef_3(ar, dec, t, Axes.getX(), Axes.getY()); 198 | Serial.println(); 199 | Serial.println("done_set3"); 200 | }else if(strcmp(comm, "goto")==0){ 201 | Coords.getHCoords(ar, dec, t, &ac, &alt); 202 | Axes.goToRads(ac, alt); 203 | Serial.print("h_");Serial.print(Axes.getX(), 6); Serial.print(' '); Serial.print(Axes.getY(), 6);Serial.println(); 204 | if(Coords.isConfigured()==true){ 205 | Coords.getECoords(ac, alt, t, &ar, &dec); 206 | Serial.print("e_");Serial.print(ar, 6); Serial.print(' '); Serial.print(dec, 6);Serial.println(); 207 | } 208 | Serial.println("done_goto"); 209 | }else if(strcmp(comm, "move")==0){ 210 | Axes.goToRads(ac, alt); 211 | Serial.print("p_");Serial.print(Axes.getPx(), DEC); Serial.print(' '); Serial.print(Axes.getPy(), DEC);Serial.println(); 212 | Serial.print("h_");Serial.print(Axes.getX(), 6); Serial.print(' '); Serial.print(Axes.getY(), 6);Serial.println(); 213 | if(Coords.isConfigured()==true){ 214 | Coords.getECoords(ac, alt, t, &ar, &dec); 215 | Serial.print("e_");Serial.print(ar, 6); Serial.print(' '); Serial.print(dec, 6);Serial.println(); 216 | } 217 | Serial.println("done_move"); 218 | }else if(strcmp(comm, "movx")==0){ 219 | while(Serial.available()<=0){} 220 | dir = Serial.read(); 221 | mov_end = Axes.movx((dir == '1')); 222 | Serial.print("h_");Serial.print(Axes.getX(), 6); Serial.print(' '); Serial.print(Axes.getY(), 6);Serial.println(); 223 | if(Coords.isConfigured()==true){ 224 | Coords.getECoords(Axes.getX(), Axes.getY(), t, &ar, &dec); 225 | Serial.print("e_");Serial.print(ar, 6); Serial.print(' '); Serial.print(dec, 6);Serial.println(); 226 | } 227 | if(mov_end==false) 228 | Serial.println("done_movx"); 229 | else 230 | Serial.println("done_end"); 231 | }else if(strcmp(comm, "movy")==0){ 232 | while(Serial.available()<=0){} 233 | dir = Serial.read(); 234 | mov_end = Axes.movy((dir == '1')); 235 | Serial.print("h_");Serial.print(Axes.getX(), 6); Serial.print(' '); Serial.print(Axes.getY(), 6);Serial.println(); 236 | if(Coords.isConfigured()==true){ 237 | Coords.getECoords(Axes.getX(), Axes.getY(), t, &ar, &dec); 238 | Serial.print("e_");Serial.print(ar, 6); Serial.print(' '); Serial.print(dec, 6);Serial.println(); 239 | } 240 | if(mov_end==false) 241 | Serial.println("done_movy"); 242 | else 243 | Serial.println("done_end"); 244 | }else if(strcmp(comm, "init")==0){ 245 | Axes.init(); 246 | Serial.println(); 247 | Serial.print("p_");Serial.print(Axes.getPX(), DEC); Serial.print(' '); Serial.print(Axes.getPY(), DEC);Serial.println(); 248 | Serial.println("done_init"); 249 | }else if(strcmp(comm, "laon")==0){ 250 | laserOn(); 251 | Serial.println(); 252 | Serial.println("done_laserOn"); 253 | }else if(strcmp(comm, "loff")==0){ 254 | laserOff(); 255 | Serial.println(); 256 | Serial.println("done_laserOff"); 257 | }else if(strcmp(comm, "stop")==0){ 258 | Serial.println("done_stop"); 259 | }else 260 | Serial.println("ERROR"); 261 | } 262 | 263 | -------------------------------------------------------------------------------- /main/python/coords.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import math 5 | import re 6 | import logging 7 | from time import time, ctime, strftime, localtime 8 | 9 | # \brief Functions library for format conversions. 10 | # 11 | # Contains the necessary functions to calculate most commons format conversions used by the communications 12 | # with the device and Stellarium. 13 | 14 | 15 | ## From radians to hours, with until six decimals of precision (float) 16 | # (rads * 180)/(15 * pi) 17 | # 18 | # \param rads Radians in float format 19 | # \return Float that represents the number of hours equivalent to the received radians 20 | def rad_2_hour(rads): 21 | h = round( (rads * 180)/(15 * math.pi), 6) 22 | if h > 24.0: 23 | h = h - 24.0 24 | if h < 0.0: 25 | h = 24.0 + h 26 | return h 27 | 28 | ## Transforms from radians in a string format to degrees (float) 29 | # (rad * 180)/pi 30 | # 31 | # \param rad Signed radians in string format 32 | # \return Degrees in float format 33 | def radStr_2_deg(rad): 34 | exp = re.compile('^(-?)[0-9]{1}\.[0-9]{4,8}') 35 | 36 | if(not exp.match(rad)): 37 | return None 38 | 39 | r = float(rad) 40 | if(r < 0): 41 | r = (2 * math.pi) - abs(r) 42 | 43 | return (r * 180) / math.pi 44 | 45 | ## Transforms radians from float to string format 46 | # 47 | # \param rad Radians in float format 48 | # \return Signed radians in string format 49 | def rad_2_radStr(rad): 50 | if(rad < 0.0): return '%f' % rad; 51 | else: return '+%f' % rad; 52 | 53 | ## Transforms from radians to degrees, both in string format 54 | # 55 | # \param r Signed radians in string format 56 | # \return Degrees in string format (ej: "DºM'S''") 57 | def radStr_2_degStr(r): 58 | return deg_2_degStr(radStr_2_deg(r)) 59 | 60 | ## Tranforms from degrees in string format to radians 61 | # d = DºM'S'' => D+(M/60)+(S/60^2) degrees => D.dº 62 | # 63 | # \param d Degrees in string format ("DºM'S''" || "D.dº") 64 | # \return Radians in float format 65 | def degStr_2_rad(d): 66 | exp1 = re.compile('^-?[0-9]{,3}(º|ᵒ)[0-9]{,3}\'[0-9]{,3}([\']{2}|")$') 67 | exp2 = re.compile('^-?[0-9]{,3}\.[0-9]{,6}(º|ᵒ)$') 68 | 69 | if(not exp1.match(d) and not exp2.match(d)): 70 | logging.debug("Error parametro: %s" % d) 71 | return None 72 | elif(exp1.match(d)): 73 | d = d.replace('º','.').replace("''",'.').replace("'",'.') 74 | d_dic = d.split('.') 75 | d_deg = float(d_dic[0]) 76 | d_min = float(d_dic[1]) 77 | d_sec = float(d_dic[2]) 78 | 79 | if(d_deg < 0): 80 | d_min = 0 - d_min; 81 | d_sec = 0 - d_sec; 82 | 83 | d_ndeg = (d_deg+(d_min/60)+(d_sec/(60**2))) 84 | else: 85 | d_ndeg = float(d.replace('º','')) 86 | if(d_ndeg < 0): d_ndeg = 360 - abs(d_ndeg); 87 | 88 | return round((d_ndeg * math.pi) / 180, 6) 89 | 90 | ## Transforms from degrees to radians, both in string format 91 | # 92 | # \param d Degrees in string format ("DºM'S''" || "D.dº") 93 | # \return Signed radians in string format 94 | def degStr_2_radStr(d): 95 | return rad_2_radStr(degStr_2_rad(d)) 96 | 97 | ## Transforms degrees from float to string format. 98 | # 99 | # \param deg Degrees in float format 100 | # \return Degrees in string format ("DºM'S''") 101 | def deg_2_degStr(deg): 102 | ndeg = math.floor(float(deg)) 103 | 104 | nmins = (deg - ndeg) * 60 105 | mins = math.floor(nmins) 106 | secs = round( (nmins - mins) * 60 ) 107 | 108 | return "%dº%d'%d''" % (ndeg, mins, secs) 109 | 110 | ## From hours in string format to radians 111 | # h = HhMmSs => H+(M/60)+(S/60^2) hours 112 | # (hours * 15 * pi)/180 113 | # 114 | # \param h Hours in string format ("HhMmSSs") 115 | # \return Radians in float format 116 | def hourStr_2_rad(h): 117 | exp = re.compile('^[0-9]{,3}h[0-9]{,3}m[0-9]{,3}s$') 118 | if(not exp.match(h)): 119 | logging.debug("Error in param: %s" % h) 120 | return None 121 | 122 | h = h.replace('h','.').replace("m",'.').replace("s",'.') 123 | h_dic = h.split('.') 124 | 125 | h_h = float(h_dic[0]) 126 | h_m = float(h_dic[1]) 127 | h_s = float(h_dic[2]) 128 | 129 | nh = (h_h+(h_m/60)+(h_s/(60**2))) 130 | 131 | return round((nh * 15 * math.pi) / 180, 6) 132 | 133 | ## Transforms hours from float to string format 134 | # 135 | # \param hours Hours in float format 136 | # \return Hours in string format ("HhMmSSs") 137 | def hour_2_hourStr(hours): 138 | (h, m, s) = hour_min_sec(hours) 139 | return '%dh%dm%00.1fs' % (h, m, s) 140 | 141 | ## From hours in float format, to a list with number of hours, minutes and seconds 142 | # 143 | # \param hours Hours in float format 144 | # \return List with (hours, minutes, seconds) 145 | def hour_min_sec(hours): 146 | h = math.floor(hours) 147 | 148 | hours_m = (hours - h)*60.0 149 | m = math.floor(hours_m) 150 | 151 | s = (hours_m - m)*60.0 152 | 153 | #Avoiding the X.60 values 154 | if s >= 59.99: 155 | s = 0 156 | m += 1 157 | if m >= 60: 158 | m = 60-m 159 | h += 1 160 | 161 | return (h, m, s) 162 | 163 | ## From degrees in float format, to a list with number of degrees, minutes and seconds 164 | # 165 | # \param degs Degrees in float format 166 | # \return List with (degrees, minutes, seconds) 167 | def grad_min_sec(degs): 168 | #Avoiding operations with negative values 169 | to_neg = False 170 | if degs < 0: 171 | degs = math.fabs(degs) 172 | to_neg = True 173 | 174 | d = math.floor(degs) 175 | 176 | degs_m = (degs - d)*60.0 177 | m = math.floor(degs_m) 178 | 179 | s = (degs_m - m)*60.0 180 | 181 | #Avoiding the .60 values 182 | if s >= 59.99: 183 | s = 0 184 | m += 1 185 | if m >= 60.0: 186 | m = 60.0-m 187 | d += 1 188 | 189 | if to_neg: 190 | d = -d; 191 | 192 | return (d, m, s) 193 | 194 | ## Transforms the values obtained from "Stellarium Telescope Protocol", to a list with each value in string format 195 | # ("HhMmSSs", "DºM'S''", "HhMmSs") 196 | # 197 | # \param ra Right ascension 198 | # \param dec Declination 199 | # \param mtime Timestamp in microseconds 200 | # \return List with (Right ascension, declination, time) => ("HhMmSSs", "DºM'S''", "HhMmSs") 201 | def eCoords2str(ra, dec, mtime): 202 | ra_h = ra*12.0/2147483648 203 | dec_d = dec*90.0/1073741824 204 | time_s = math.floor(mtime / 1000000) 205 | 206 | return ('%dh%dm%00.0fs' % hour_min_sec(ra_h), '%dº%d\'%00.0f\'\'' % grad_min_sec(dec_d), strftime("%Hh%Mm%Ss", localtime(time_s))) 207 | 208 | ## Transforms coordinates from radians to J2000 string format ("HhMmSSs/GºM'SS'' at Fecha") 209 | # 210 | # \param ra Right ascension (float) 211 | # \param dec Declination (float) 212 | # \param mtime Timestamp in microseconds (float) 213 | # \return Equiatorial coordinates in J2000 string format 214 | def toJ2000(ra, dec, mtime): 215 | # HhMmSs => H+(M/60)+(S/60^2) hours 216 | # DºM'S'' => D+(M/60)+(S/60^2) degrees 217 | # From hours to radians: (hours * 15 * pi)/180 218 | 219 | ra_h = ra*12.0/2147483648 220 | (h1, m1, s1) = hour_min_sec(ra_h) 221 | 222 | dec_d = dec*90.0/1073741824 223 | (h2, m2, s2) = grad_min_sec(dec_d) 224 | 225 | time_s = math.floor(mtime / 1000000) # From microseconds to seconds (Unix timestamp) 226 | t = ctime(time_s) 227 | 228 | return '%dh%dm%00.0fs/%dº%d\'%00.1f\'\' at %s' % (h1, m1, s1, h2, m2, s2, t) 229 | 230 | 231 | ## Transforms coordinates from radians to the "Stellarium Telescope Protocol" format 232 | # 233 | # \param ra Right ascension (float) 234 | # \param dec Declination (float) 235 | # \return List with (Right ascension, Declination) in the "Stellarium Telescope Protocol" format 236 | def rad_2_stellarium_protocol(ra, dec): 237 | 238 | ra_h = rad_2_hour(ra) 239 | 240 | dec_d = (dec * 180) / math.pi 241 | 242 | logging.debug("(hours, degrees): (%f, %f)" % (ra_h, dec_d)) 243 | 244 | return (int(ra_h*(2147483648/12.0)), int(dec_d*(1073741824/90.0))) 245 | 246 | -------------------------------------------------------------------------------- /main/python/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate UI 4 | pyuic4 ./ui/laser_control.ui -o ./ui/laser_control_ui.py 5 | pyrcc4 ./ui/icons_set.qrc -o ./ui/icons_set_rc.py 6 | 7 | -------------------------------------------------------------------------------- /main/python/laser_control_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -d 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import signal, os 6 | import functools 7 | import logging 8 | from PyQt4 import QtCore, QtGui 9 | from threading import Thread, Event 10 | from time import ctime, strftime, localtime 11 | from ui.laser_control_ui import Ui_LaserControl 12 | from telescope_server import Telescope_Server 13 | import coords 14 | from ldevice import LaserDev, get_avalilable_ports 15 | from repeat_timer import RepeatTimer 16 | 17 | 18 | try: 19 | _fromUtf8 = QtCore.QString.fromUtf8 20 | except AttributeError: 21 | _fromUtf8 = lambda s: s 22 | 23 | try: 24 | _toUtf8 = QtCore.QString.toUtf8 25 | except AttributeError: 26 | _toUtf8 = lambda s: s 27 | 28 | logging.basicConfig(level=logging.DEBUG, format="%(filename)s: %(funcName)s - %(levelname)s: %(message)s") 29 | 30 | 31 | ## \brief Main class that coordinates all the necessary elements and the user interface 32 | # 33 | # Uses the Telescope_Server and LaserDev modules in order to coordinate the communications between the 34 | # elements and the user interface with PyQt4 35 | # 36 | class LaserControlMain(QtGui.QMainWindow): 37 | ## @var act_stell_pos 38 | # Signal to communications with the Telescope_Server instance 39 | # It emits when we want to send to Stellarium the equatorial coordinates 40 | act_stell_pos = QtCore.pyqtSignal(str, str) 41 | 42 | ## Class constructor 43 | # 44 | # Starts the UI and the Telescope_Server instance 45 | # 46 | # \param parent (optional) Parent object. By default is None 47 | def __init__(self, parent=None): 48 | super(LaserControlMain, self).__init__(parent) 49 | self.confMode = False 50 | self.nRef = 0 51 | self.device = None 52 | (self._ra, self._dec) = ("0h0m0s", "0º0'0''") 53 | 54 | self.ui = Ui_LaserControl() 55 | self.ui.setupUi(self) 56 | self.ui.Reconfigure.setVisible(False) 57 | 58 | regex=QtCore.QRegExp("%s" % (_fromUtf8("^-?[0-9]{1,3}(º|ᵒ)[0-9]{1,3}'[0-9]{1,3}([']{2}|\")$"))) 59 | self.Valid=QtGui.QRegExpValidator(regex, self) 60 | self.ui.posHorizontal.setValidator(self.Valid) 61 | self.ui.posVertical.setValidator(self.Valid) 62 | self.ui.posHorizontal.setText("%s" % (_fromUtf8("0º0'0''"))) 63 | self.ui.posVertical.setText("%s" % (_fromUtf8("0º0'0''"))) 64 | self.ui.tabWidget.setCurrentIndex(1) 65 | self.ui.tabWidget.setTabEnabled(1, False) 66 | 67 | self.pos = ('0.0000', '0.0000') 68 | self._prev_pos = ("0º0'0''", "0º0'0''") 69 | 70 | #Starts server 71 | self.Server = Telescope_Server(pos_signal=self.act_stell_pos) 72 | self.Server.daemon = True 73 | self.Server.start() 74 | 75 | self.setSignals() 76 | self.setShortcuts() 77 | 78 | #At the beginning, configuration mode is On 79 | self.ui.confMode.setChecked(True) 80 | 81 | ## Connects the UI signals 82 | # 83 | def setSignals(self): 84 | #Movements 85 | QtCore.QObject.connect(self.ui.upButton, QtCore.SIGNAL("pressed()"), self.upPressed) 86 | QtCore.QObject.connect(self.ui.downButton, QtCore.SIGNAL("pressed()"), self.downPressed) 87 | QtCore.QObject.connect(self.ui.rightButton, QtCore.SIGNAL("pressed()"), self.rightPressed) 88 | QtCore.QObject.connect(self.ui.leftButton, QtCore.SIGNAL("pressed()"), self.leftPressed) 89 | QtCore.QObject.connect(self.ui.upButton, QtCore.SIGNAL("released()"), self.arrow_released) 90 | QtCore.QObject.connect(self.ui.downButton, QtCore.SIGNAL("released()"), self.arrow_released) 91 | QtCore.QObject.connect(self.ui.rightButton, QtCore.SIGNAL("released()"), self.arrow_released) 92 | QtCore.QObject.connect(self.ui.leftButton, QtCore.SIGNAL("released()"), self.arrow_released) 93 | 94 | #Position 95 | QtCore.QObject.connect(self.ui.posVertical, QtCore.SIGNAL("editingFinished()"), self.posChanged) 96 | QtCore.QObject.connect(self.ui.posHorizontal, QtCore.SIGNAL("editingFinished()"), self.posChanged) 97 | 98 | #Laser 99 | QtCore.QObject.connect(self.ui.laserOn, QtCore.SIGNAL("toggled(bool)"), self.laserToggled) 100 | 101 | #Options 102 | QtCore.QObject.connect(self.ui.confMode, QtCore.SIGNAL("toggled(bool)"), self.confModeChanged) 103 | QtCore.QObject.connect(self.ui.trackMode, QtCore.SIGNAL("toggled(bool)"), self.trackModeChanged) 104 | 105 | #Device connection 106 | self.refreshSerialPorts() 107 | QtCore.QObject.connect(self.ui.action_Refresh, QtCore.SIGNAL("triggered(bool)"), self.refreshSerialPorts) 108 | QtCore.QObject.connect(self.ui.action_Desconectar, QtCore.SIGNAL("triggered(bool)"), self.closeDevice) 109 | #El dispositivo debe recalcular el número de pasos por vuelta en cada eje 110 | QtCore.QObject.connect(self.ui.action_Recalibrar, QtCore.SIGNAL("triggered(bool)"), self.initDevice) 111 | 112 | #Stellarium.. 113 | self.Server.stell_pos_recv.connect(self.stellariumRecv) 114 | 115 | ## Show available serial ports and set connection signals 116 | # 117 | def refreshSerialPorts(self): 118 | # Clear menu.. 119 | self.ui.menu_Connect.clear() 120 | self.ui.menu_Connect.addAction(self.ui.action_Refresh) 121 | self.ui.menu_Connect.addSeparator() 122 | 123 | port_list = get_avalilable_ports() 124 | for device_path in port_list: 125 | act = QtGui.QAction(device_path, self) 126 | act.triggered.connect(functools.partial(self.connectDevice, device_path)) 127 | self.ui.menu_Connect.addAction(act) 128 | 129 | ## Shortcuts for the device movements 130 | # 131 | def setShortcuts(self): 132 | self.ui.leftB_shortCut = QtGui.QShortcut(QtGui.QKeySequence("Shift+Left"), self, self.ui.leftButton.animateClick) 133 | self.ui.rightB_shortCut = QtGui.QShortcut(QtGui.QKeySequence("Shift+Right"), self, self.ui.rightButton.animateClick) 134 | self.ui.upB_shortCut = QtGui.QShortcut(QtGui.QKeySequence("Shift+Up"), self, self.ui.upButton.animateClick) 135 | self.ui.downB_shortCut = QtGui.QShortcut(QtGui.QKeySequence("Shift+Down"), self, self.ui.downButton.animateClick) 136 | 137 | ## Handles the coordinate reception from Stellarium 138 | # 139 | # Receives the coordinates from Stellarium by the Telescope_Server instance. 140 | # If the device is connected, sends the coordinates to it, as either the configuration or movement values. 141 | # 142 | # Also manages the UI status along the configuration process. 143 | # 144 | # \param ra Right ascension 145 | # \param dec Declination 146 | # \param mtime Timestamp 147 | def stellariumRecv(self, ra, dec, mtime): 148 | ra = float(ra) 149 | dec = float(dec) 150 | mtime = float(mtime) 151 | logging.debug("%s" % coords.toJ2000(ra, dec, mtime)) 152 | (sra, sdec, stime) = coords.eCoords2str(ra, dec, mtime) 153 | (self._ra, self._dec) = (sra, sdec) 154 | 155 | if self.device != None: 156 | logging.debug("Sending to the device: '%s','%s','%s'" % (sra, sdec, stime)) 157 | try: 158 | if self.ui.Reconfigure.isChecked(): 159 | if self.ui.redef_1: 160 | self.ui.redef_1.setChecked(False) 161 | redef = 1 162 | elif self.ui.redef_2: 163 | self.ui.redef_2.setChecked(False) 164 | redef = 2 165 | else: 166 | self.ui.redef_3.setChecked(False) 167 | redef = 3 168 | self.ui.Reconfigure.setChecked(False) 169 | self.device.setRef(redef, sra, sdec, stime) 170 | elif not self.confMode: 171 | self.device.goto(sra, sdec, stime) 172 | else: 173 | self.nRef = self.nRef + 1 174 | self.ui.text_status.setText("References: %d/2" % self.nRef) 175 | self.device.setRef(self.nRef, sra, sdec, stime) 176 | if self.nRef == 2: 177 | self.setConfigDone() 178 | self.nRef = 0 179 | except: 180 | logging.debug("Device not found..") 181 | e = sys.exc_info()[1] 182 | print("Error: %s" % e) 183 | 184 | ## Up key pushed 185 | # 186 | # Starts the upward movement of the device 187 | def upPressed(self): 188 | if self.device != None: 189 | self.device.movy('1') 190 | 191 | ## Down key pushed 192 | # 193 | # Starts the downward movement 194 | def downPressed(self): 195 | if self.device != None: 196 | self.device.movy('0') 197 | 198 | ## Right key pushed 199 | # 200 | # Starts the clockwise movement 201 | def rightPressed(self): 202 | if self.device != None: 203 | self.device.movx('1') 204 | 205 | ## Left key pushed 206 | # 207 | # Starts the counter clockwise movement 208 | def leftPressed(self): 209 | if self.device != None: 210 | self.device.movx('0') 211 | 212 | ## Up/Down/Right/Left key released.. 213 | # 214 | # Stops any movement 215 | def arrow_released(self): 216 | if self.device != None: 217 | self.device.stop() 218 | 219 | ## Handles the changes on the coordinates text boxes 220 | # 221 | # If the device is configured, sends the new coordinates to it 222 | def posChanged(self): 223 | logging.debug("(%s, %s)" % (self.ui.posHorizontal.text(), self.ui.posVertical.text())) 224 | x = _toUtf8(self.ui.posHorizontal.text()) 225 | y = _toUtf8(self.ui.posVertical.text()) 226 | if self.device != None and (self._prev_pos[0]!=x or self._prev_pos[1]!=y): 227 | logging.debug("Sending (%s, %s) to device" % (x, y)) 228 | self.device.move(x,y) 229 | self._prev_pos = (x, y) 230 | 231 | ## Handles the changes on "configuration mode" check box 232 | # 233 | def confModeChanged(self): 234 | if self.ui.confMode.isChecked(): 235 | self.confMode = True 236 | self.nRef = 0 237 | logging.debug("Conf mode ON") 238 | else: 239 | self.setConfigDone() 240 | self.confMode = False 241 | logging.debug("Conf mode Off") 242 | 243 | ## Handles the end of the device configuration process 244 | # 245 | def setConfigDone(self): 246 | self.ui.confMode.setChecked(False) 247 | self.ui.tabWidget.setTabEnabled(1, True) 248 | self.ui.tabWidget.setCurrentIndex(1) 249 | self.ui.text_status.setText("References: 2/2") 250 | 251 | self.ui.confMode.setVisible(False) 252 | self.ui.textEdit.setVisible(False) 253 | 254 | self.ui.Reconfigure.setVisible(True) 255 | 256 | 257 | ## Handles changes on tracking check box 258 | # 259 | # If check is On, starts the tracking mode on the device 260 | def trackModeChanged(self): 261 | if self.ui.trackMode.isChecked(): 262 | self.track = RepeatTimer(5.0, self.tracking) 263 | self.track.start() 264 | logging.debug("Track mode ON") 265 | else: 266 | self.track.cancel() 267 | logging.debug("Track mode Off") 268 | 269 | ## Starts the device connection 270 | # 271 | # In case of error, shows the message on UI 272 | # \param device_path USB Serial Device path 273 | def connectDevice(self, device_path): 274 | self.ui.action_Desconectar.setEnabled(True) 275 | self.ui.action_Recalibrar.setEnabled(True) 276 | logging.info("Connecting to device via '%s'" % device_path) 277 | try: 278 | if self.device == None: 279 | self.device = LaserDev(usb_serial=device_path) 280 | self.device.init_received.connect(self.init_received) 281 | self.device.pos_received.connect(self.pos_received) 282 | self.device.pos_e_received.connect(self.pos_e_received) 283 | self.device.start() 284 | except: 285 | logging.info("Device not found") 286 | QtGui.QMessageBox.warning(self, 'Warning', "Device not found") 287 | self.ui.action_Desconectar.setEnabled(False) 288 | self.ui.action_Recalibrar.setEnabled(False) 289 | self.device = None 290 | 291 | ## Initialize device 292 | # 293 | # The device will calculate the steps per revolution 294 | def initDevice(self): 295 | logging.info("Initializing device..") 296 | try: 297 | if self.device != None: 298 | self.device.init() 299 | except: 300 | logging.info("Error initializing device.") 301 | 302 | ## Receives the end of initialization signal from the device 303 | # 304 | # That signal indicates that the device is successfully initialized 305 | def init_received(self): 306 | logging.debug("Init received") 307 | self.pos = ('0.0000', '0.0000') 308 | self.ui.posHorizontal.setText("%s" % _fromUtf8("0º0'0''")) 309 | self.ui.posVertical.setText("%s" % _fromUtf8("0º0'0''")) 310 | 311 | ## Receives the position updated signal from the device 312 | # 313 | # The parameters are the horizontal coordinates which the device points to 314 | def pos_received(self, x, y): 315 | logging.debug("%s,%s" % (x, y)) 316 | self.pos = (x, y) 317 | self.ui.posHorizontal.setText("%s" % _fromUtf8(coords.deg_2_degStr(360.0 - coords.radStr_2_deg(self.pos[0])))) 318 | self.ui.posVertical.setText("%s" % _fromUtf8(coords.radStr_2_degStr(self.pos[1]))) 319 | 320 | ## Receives the position updated signal from the device 321 | # 322 | # The parameters are the equatorial coordinates which the device points to 323 | def pos_e_received(self, x, y): 324 | logging.debug("%s,%s" % (x, y)) 325 | self.act_stell_pos.emit(x, y) 326 | 327 | ## Tracking mode 328 | # 329 | # Updates periodically the device position by sending the equatorial coordinates and time 330 | def tracking(self): 331 | logging.debug("('%s', '%s', '%s')" % (self._ra, self._dec, strftime("%Hh%Mm%Ss", localtime()))) 332 | if self.device != None and self._ra != '0h0m0s': 333 | self.device.goto( self._ra, self._dec, strftime("%Hh%Mm%Ss", localtime()) ) 334 | 335 | ## Laser toggle.. 336 | # 337 | def laserToggled(self): 338 | if self.ui.laserOn.isChecked(): 339 | if self.device != None: 340 | self.device.laserOn() 341 | logging.debug("Laser ON") 342 | else: 343 | if self.device != None: 344 | self.device.laserOff() 345 | logging.debug("Laser Off") 346 | 347 | ## Close the device connection 348 | # 349 | def closeDevice(self): 350 | self.ui.action_Conectar.setEnabled(True) 351 | self.ui.action_Desconectar.setEnabled(False) 352 | self.ui.action_Recalibrar.setEnabled(False) 353 | logging.info("Disconnecting device..") 354 | try: 355 | if self.device != None: 356 | self.device.close() 357 | self.device = None 358 | except: 359 | self.device = None 360 | 361 | ## Exit.. 362 | # 363 | def closeEvent(self, event): 364 | logging.debug("Bye!") 365 | try: 366 | self.Server.close_socket() 367 | self.track.cancel() 368 | event.accept() 369 | except: 370 | event.accept() 371 | 372 | 373 | if __name__ == "__main__": 374 | app = QtGui.QApplication(sys.argv) 375 | app_gui = LaserControlMain() 376 | app_gui.show() 377 | sys.exit(app.exec_()) 378 | 379 | -------------------------------------------------------------------------------- /main/python/ldevice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import re 6 | import math 7 | import serial, os 8 | from serial.tools import list_ports 9 | import logging 10 | from PyQt4 import QtCore 11 | import asyncore, socket 12 | from time import sleep, time, strftime, localtime 13 | from string import replace 14 | import coords 15 | 16 | # Check for pyserial version ( >= 2.6 nedded) 17 | if serial.VERSION < '2.6': 18 | print("pySerial >= 2.6. is needed (in Linux you can install 'pip' and then run 'pip install pyserial --upgrade' as root)") 19 | sys.exit() 20 | 21 | ## \brief Class that implements the interface to control the device. 22 | # 23 | # It is designed to work on a separate thread. The communication with the main application is via 24 | # asynchronous Qt signals. 25 | # Also it uses some functions present in coords.py 26 | # 27 | class LaserDev (QtCore.QThread): 28 | ## @var init_received 29 | # Signal for the communications with the main thread 30 | # It emmits when the device is successfully initialized 31 | init_received = QtCore.pyqtSignal() #X, Y 32 | 33 | ## @var pos_received 34 | # Signal for the communications with the main thread 35 | # It emits when the horizontal coordinates are received from the device 36 | pos_received = QtCore.pyqtSignal(str, str) #alt, az 37 | 38 | ## @var pos_e_received 39 | # Signal for the communications with the main thread 40 | # It emits when the equatorial coordinates are received from the device 41 | pos_e_received = QtCore.pyqtSignal(str, str) #ar, dec 42 | 43 | 44 | ## Class constructor 45 | # 46 | # Sets the device connection by the serial port 47 | # 48 | # \param usb_serial Serial port. By default is '/dev/ttyUSB0' 49 | # \param usb_serial_port Transmission speed. Default 9600 baud 50 | # \param timeout Maximum waiting time for responses 51 | def __init__(self, usb_serial='/dev/ttyUSB0', usb_serial_baud=9600, timeout=2): 52 | QtCore.QThread.__init__(self, None) 53 | self.serial = serial.Serial(usb_serial, usb_serial_baud, timeout=timeout) 54 | logging.debug("Connected (%s)" % usb_serial) 55 | 56 | ## Getting the response of sent commands 57 | # 58 | # Implements the common process for getting response of a command 59 | # 60 | # \param expect Regular expression for the "end of the command" tag. By default is '^cmd$' 61 | # \param wait Maximum number of cycles to wait for the response 62 | # \return Last line read 63 | def sread(self, expect='^cmd$', wait=0): 64 | _d = None 65 | exp = re.compile(expect) 66 | line = self.serial.readline().rstrip() 67 | tag_steps = re.compile('^p_.*$') 68 | tag_horizontal = re.compile('^h_.*$') 69 | tag_equatorial = re.compile('^e_.*$') 70 | _count = 0 71 | while(not exp.match(line) and _count <= wait): 72 | if tag_steps.match(line): 73 | resp = line.replace("p_", '') 74 | _d = resp.split(' ') 75 | logging.debug("Steps: (%s, %s)" % (_d[0], _d[1])) 76 | elif tag_horizontal.match(line): 77 | resp = line.replace("h_", '') 78 | _d = resp.split(' ') 79 | self.pos_received.emit(_d[0], _d[1]) 80 | logging.debug("PosH: (%s / %s)" % ( coords.deg_2_degStr( 360.0 - coords.radStr_2_deg(_d[0])), coords.deg_2_degStr( coords.radStr_2_deg(_d[1])) )) 81 | elif tag_equatorial.match(line): 82 | resp = line.replace("e_", '') 83 | _d = resp.split(' ') 84 | self.pos_e_received.emit(_d[0], _d[1]) 85 | logging.debug("PosE: (%s / %s)" % ( \ 86 | coords.hour_2_hourStr(coords.rad_2_hour(coords.degStr_2_rad(coords.radStr_2_degStr(_d[0])))), \ 87 | coords.deg_2_degStr(coords.radStr_2_deg(_d[1])) )) 88 | if line == '': 89 | _count += 1 90 | else: 91 | print("__debug__: %s" % line) 92 | line = self.serial.readline().rstrip() 93 | return line 94 | 95 | ## Sets initial time in the device 96 | # 97 | # \param time Unix timestamp 98 | def setTime(self, time): 99 | self.serial.write("time") 100 | if(self.serial.readline().rstrip() == 'float'): 101 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(time)) ) 102 | self.sread() 103 | 104 | ## Initializes the device 105 | # 106 | # Emits the init_received when the device responds 107 | def init(self): 108 | self.serial.readline() # wait the timeout at most 109 | self.serial.write('init') 110 | 111 | self.sread(expect='^done_init$', wait=20) 112 | self.init_received.emit() 113 | 114 | ## Initializes the execution thread 115 | # 116 | def run(self): 117 | self.init() 118 | 119 | ## Sets a reference object in the device 120 | # 121 | # 122 | # \param id_rel Number of the reference object, 1, 2 or 3 123 | # \param ra Right ascension 124 | # \param dec Declination 125 | # \param time Timestamp of the measure 126 | def setRef(self, id_ref, ra, dec, time): 127 | setf = {1: 'set1', 2: 'set2', 3: 'set3'} 128 | logging.debug(" %s(%s, %s, %s)" % (setf[id_ref], ra, dec, time)) 129 | self.serial.write(setf[id_ref]) 130 | if(self.serial.readline().rstrip() == 'float'): 131 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(ra)) ) 132 | self.serial.write( coords.rad_2_radStr(coords.degStr_2_rad(dec)) ) 133 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(time)) ) 134 | self.sread(wait=5) 135 | 136 | ## Points the device toward the given equatorial coordinates 137 | # 138 | # \param ra Right ascension 139 | # \param dec Declination 140 | # \param time Timestamp of the measure 141 | def goto(self, ra, dec, time): 142 | logging.debug("(%s, %s, %s)" % (ra, dec, time)) 143 | self.serial.write('goto') 144 | if(self.serial.readline().rstrip() == 'float'): 145 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(ra)) ) 146 | self.serial.write( coords.rad_2_radStr(coords.degStr_2_rad(dec)) ) 147 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(time)) ) 148 | self.sread(wait=10) 149 | 150 | ## Points the device toward the given horizontal coordinates 151 | # 152 | # \param ac Azimut 153 | # \param alt Altitude 154 | def move(self, ac, alt): 155 | logging.debug("(%s, %s)" % (ac, alt)) 156 | self.serial.write('move') 157 | if(self.serial.readline().rstrip() == 'float'): 158 | self.serial.write( coords.rad_2_radStr(6.283185 - coords.degStr_2_rad(ac)) ) 159 | self.serial.write( coords.rad_2_radStr(coords.degStr_2_rad(alt)) ) 160 | self.serial.write( coords.rad_2_radStr(coords.hourStr_2_rad(strftime("%Hh%Mm%Ss", localtime()))) ) 161 | self.sread(wait=10) 162 | 163 | ## Starts the accelerated movement along the X axis, in the given direction 164 | # 165 | # \param signDir Direction of movement: 1 means clockwise direction, 0 means counter clockwise 166 | def movx(self, signDir): 167 | self.serial.write('movx') 168 | self.serial.write(signDir) 169 | 170 | ## Starts the accelerated movement along the Y axis, in the given direction 171 | # 172 | # \param signDir Direction of movement. 1 means upwards, 0 means downwards 173 | def movy(self, signDir): 174 | self.serial.write('movy') 175 | self.serial.write(signDir) 176 | 177 | ## Stops the accelerated movement on both axes 178 | # 179 | def stop(self): 180 | self.serial.write('stop') 181 | resp = self.sread(expect = '^done_.*') 182 | if resp == 'done_end':# End sensor reached .. 183 | self.sread()#.. 184 | self.sread()#.. so we expect an extra cmd 185 | else: 186 | self.sread() 187 | 188 | ## Turn the laser On 189 | # 190 | def laserOn(self): 191 | self.serial.write('laon') 192 | self.sread(wait=3) 193 | 194 | ## Turn the laser Off 195 | # 196 | def laserOff(self): 197 | self.serial.write('loff') 198 | self.sread(wait=3) 199 | 200 | # Function to scan available serial ports... 201 | # 202 | # \return List of available serial ports 203 | def get_avalilable_ports(): 204 | available = [] 205 | 206 | ## Windows (I cannot test this part..) 207 | #if os.name == 'nt': 208 | # for i in range(256): 209 | # try: 210 | # s = serial.Serial(i) 211 | # available.append('COM'+str(i + 1)) 212 | # s.close() 213 | # except serial.SerialException: 214 | # pass 215 | ## Mac / Linux 216 | #else: 217 | 218 | # We only want the USB-Serial ports: 219 | regex = re.compile('/dev/tty(USB|ACM)[0-9]+') 220 | 221 | for port in list_ports.comports(): 222 | if regex.match(port[0]): 223 | available.append(port[0]) 224 | 225 | return available 226 | -------------------------------------------------------------------------------- /main/python/repeat_timer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | from threading import Thread, Event 6 | 7 | ## \brief Auxiliary Class to execute functions periodically. 8 | # 9 | class RepeatTimer(Thread): 10 | 11 | ## Class constructor. 12 | # 13 | # Executes the given function each interval time, a certain number of times (iterations). 14 | # 15 | # \param interval Interval, in seconds 16 | # \param function Function or callable object to execute 17 | # \param iterations Maximum number of iterations 18 | # \param args Function arguments 19 | # \param kwargs Arguments cictionary 20 | def __init__(self, interval, function, iterations=0, args=[], kwargs={}): 21 | Thread.__init__(self) 22 | self.interval = interval 23 | self.function = function 24 | self.iterations = iterations 25 | self.args = args 26 | self.kwargs = kwargs 27 | self.finished = Event() 28 | 29 | ## Statrs thread 30 | # 31 | def run(self): 32 | count = 0 33 | while not self.finished.is_set() and (self.iterations <= 0 or count < self.iterations): 34 | self.finished.wait(self.interval) 35 | if not self.finished.is_set(): 36 | self.function(*self.args, **self.kwargs) 37 | count += 1 38 | 39 | ## Cancel execution 40 | # 41 | def cancel(self): 42 | self.finished.set() 43 | -------------------------------------------------------------------------------- /main/python/telescope_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | from PyQt4 import QtCore 6 | from threading import Thread 7 | import asyncore, socket 8 | from time import time, localtime 9 | from bitstring import BitArray, BitStream, ConstBitStream # http://code.google.com/p/python-bitstring/ 10 | import coords 11 | 12 | 13 | logging.basicConfig(level=logging.DEBUG, format="%(filename)s: %(funcName)s - %(levelname)s: %(message)s") 14 | 15 | 16 | ## \brief Implementation of the server side connection for 'Stellarium Telescope Protocol' 17 | # 18 | # Manages the execution thread to the server side connection with Stellarium 19 | class Telescope_Channel(QtCore.QThread, asyncore.dispatcher): 20 | ## @var stell_pos_recv 21 | # It emits when equatorial coordinates are received from client (Stellarium) 22 | stell_pos_recv = QtCore.pyqtSignal(str, str, str) #Ra, Dec, Time 23 | 24 | ## Class constructor 25 | # 26 | # \param conn_sock Connection socket 27 | def __init__(self, conn_sock): 28 | self.is_writable = False 29 | self.buffer = '' 30 | asyncore.dispatcher.__init__(self, conn_sock) 31 | QtCore.QThread.__init__(self, None) 32 | 33 | ## Indicates the socket is readable 34 | # 35 | # \return Boolean True/False 36 | def readable(self): 37 | return True 38 | 39 | ## Indicates the socket is writable 40 | # 41 | # \return Boolean True/False 42 | def writable(self): 43 | return self.is_writable 44 | 45 | ## Close connection handler 46 | # 47 | def handle_close(self): 48 | logging.debug("Disconnected") 49 | self.close() 50 | 51 | ## Reading socket handler 52 | # 53 | # Reads and processes client data, and throws the proper signal with coordinates as parameters 54 | def handle_read(self): 55 | #format: 20 bytes in total. Size: intle:16 56 | #Incomming messages comes with 160 bytes.. 57 | data0 = self.recv(160); 58 | if data0: 59 | data = ConstBitStream(bytes=data0, length=160) 60 | #print "All: %s" % data.bin 61 | 62 | msize = data.read('intle:16') 63 | mtype = data.read('intle:16') 64 | mtime = data.read('intle:64') 65 | 66 | # RA: 67 | ant_pos = data.bitpos 68 | ra = data.read('hex:32') 69 | data.bitpos = ant_pos 70 | ra_uint = data.read('uintle:32') 71 | 72 | # DEC: 73 | ant_pos = data.bitpos 74 | dec = data.read('hex:32') 75 | data.bitpos = ant_pos 76 | dec_int = data.read('intle:32') 77 | 78 | #______ Testing: 79 | # Sends back to Stellarium the received coordinates, in order to update the field of view indicator 80 | (sra, sdec, stime) = coords.eCoords2str(float("%f" % ra_uint), float("%f" % dec_int), float("%f" % mtime)) 81 | self.act_pos(coords.hourStr_2_rad(sra), coords.degStr_2_rad(sdec)) 82 | #______ End Testing 83 | 84 | # Emits the signal with received equatorial coordinates (for use in external Qt Gui..) 85 | self.stell_pos_recv.emit("%f" % ra_uint, "%f" % dec_int, "%f" % mtime) 86 | 87 | 88 | ## Updates the field of view indicator in Stellarium 89 | # 90 | # \param ra Right ascension in signed string format 91 | # \param dec Declination in signed string format 92 | def act_pos(self, ra, dec): 93 | (ra_p, dec_p) = coords.rad_2_stellarium_protocol(float(ra), float(dec)) 94 | 95 | times = 10 #Number of times that Stellarium expects to receive new coords //Absolutly empiric.. 96 | for i in range(times): 97 | self.move(ra_p, dec_p) 98 | 99 | ## Sends to Stellarium equatorial coordinates 100 | # 101 | # Receives the coordinates in float format. Obtains the timestamp from local time 102 | # 103 | # \param ra Ascensión recta. 104 | # \param dec Declinación. 105 | def move(self, ra, dec): 106 | msize = '0x1800' 107 | mtype = '0x0000' 108 | aux_format_str = 'int:64=%r' % time() 109 | localtime = ConstBitStream(aux_format_str.replace('.', '')) 110 | 111 | sdata = ConstBitStream(msize) + ConstBitStream(mtype) 112 | sdata += ConstBitStream(intle=localtime.intle, length=64) + ConstBitStream(uintle=ra, length=32) 113 | sdata += ConstBitStream(intle=dec, length=32) + ConstBitStream(intle=0, length=32) 114 | 115 | self.buffer = sdata 116 | self.is_writable = True 117 | self.handle_write() 118 | 119 | ## Transmission handler 120 | # 121 | def handle_write(self): 122 | self.send(self.buffer.bytes) 123 | self.is_writable = False 124 | 125 | 126 | ## \brief Implementation of the server side communications for 'Stellarium Telescope Protocol'. 127 | # 128 | # Each connection request generate an independent execution thread as instance of Telescope_Channel 129 | class Telescope_Server(QtCore.QThread, asyncore.dispatcher): 130 | # @var stell_pos_recv 131 | # Proxy signal to the same name signal of the Telescope_Channel instance 132 | stell_pos_recv = QtCore.pyqtSignal(str, str, str) #Ra, Dec, Time 133 | 134 | ## Class constructor 135 | # 136 | # \param port Port to listen on 137 | # \param pos-signal Signal that will receive the coordinates to send to Stellarium 138 | def __init__(self, port=10001, pos_signal=None): 139 | asyncore.dispatcher.__init__(self, None) 140 | QtCore.QThread.__init__(self, None) 141 | self.tel = None 142 | self.port = port 143 | if pos_signal != None: 144 | pos_signal.connect(self.proxy_signal_sent) 145 | 146 | ## Starts thread 147 | # 148 | # Sets the socket to listen on 149 | def run(self): 150 | logging.info(self.__class__.__name__+" is running...") 151 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 152 | self.set_reuse_addr() 153 | self.bind(('localhost', self.port)) 154 | self.listen(1) 155 | self.connected = False 156 | asyncore.loop() 157 | 158 | ## Handles incomming connection 159 | # 160 | # Stats a new thread as Telescope_Channel instance, passing it the opened socket as parameter 161 | def handle_accept(self): 162 | self.conn, self.addr = self.accept() 163 | logging.debug('Connected: %s', self.addr) 164 | self.connected = True 165 | self.tel = Telescope_Channel(self.conn) 166 | self.tel.stell_pos_recv.connect(self.proxy_signal_recv) 167 | 168 | ## Proxy signal for receive and throw again the Telescope_Channel signal 169 | # 170 | # \param Right ascension 171 | # \param dec Declination 172 | # \param mtime Timestamp 173 | def proxy_signal_recv(self, ra, dec, mtime): 174 | self.stell_pos_recv.emit(ra, dec, mtime) 175 | 176 | ## Proxy signal for receive coordinates and send them to the Telescope_Channel threads 177 | # 178 | # \ra Right ascension 179 | # \dec Declination 180 | def proxy_signal_sent(self, ra, dec): 181 | if self.tel != None: 182 | self.tel.act_pos(ra, dec) 183 | 184 | ## Closes the connection 185 | # 186 | def close_socket(self): 187 | if self.connected: 188 | self.conn.close() 189 | 190 | #Run a Telescope Server 191 | if __name__ == '__main__': 192 | try: 193 | Server = Telescope_Server() 194 | Server.run() 195 | except KeyboardInterrupt: 196 | logging.debug("\nBye!") 197 | -------------------------------------------------------------------------------- /main/python/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/main/python/ui/__init__.py -------------------------------------------------------------------------------- /main/python/ui/icons/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/main/python/ui/icons/down.png -------------------------------------------------------------------------------- /main/python/ui/icons/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/main/python/ui/icons/left.png -------------------------------------------------------------------------------- /main/python/ui/icons/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/main/python/ui/icons/right.png -------------------------------------------------------------------------------- /main/python/ui/icons/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanrmn/Arduino-Telescope-Control/c4bcafaa4b0b07dbbbde17fd4bf7511fca5d686c/main/python/ui/icons/up.png -------------------------------------------------------------------------------- /main/python/ui/icons_set.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/down.png 4 | icons/left.png 5 | icons/right.png 6 | icons/up.png 7 | 8 | 9 | -------------------------------------------------------------------------------- /main/python/ui/icons_set_rc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Resource object code 4 | # 5 | # Created: Sun Mar 31 15:52:08 2013 6 | # by: The Resource Compiler for PyQt (Qt v4.8.1) 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt4 import QtCore 11 | 12 | qt_resource_data = "\ 13 | \x00\x00\x03\x31\ 14 | \x89\ 15 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ 16 | \x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\ 17 | \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ 18 | \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x00\x48\x00\x00\ 19 | \x00\x48\x00\x46\xc9\x6b\x3e\x00\x00\x02\xac\x49\x44\x41\x54\x48\ 20 | \xc7\xd5\x95\xbf\x6b\x14\x51\x10\xc7\xbf\xfb\xeb\x2e\xb0\x31\x49\ 21 | \x95\xc2\x20\x04\xc4\x42\xbb\x48\x1a\x03\x26\x1c\xa2\x45\x84\x58\ 22 | \x04\x0b\x2b\x7b\x6b\x9b\x05\x0b\xc1\x22\x29\xfd\x07\x2c\xd2\x08\ 23 | \x16\x36\x0a\x8b\x20\x11\x2c\xa2\x08\x36\x16\x0a\x12\x4c\xde\x7a\ 24 | \x49\x2e\xc9\x65\x93\xcb\xe5\xf6\xf6\xc7\xdb\xb7\x63\x71\xbb\xb9\ 25 | \x77\xeb\x5d\xee\x8a\x20\xf8\x60\xd8\x59\xde\xcc\xf7\x33\xb3\x6f\ 26 | \x1e\x0b\xfc\xef\x4b\xe9\x17\x30\x39\xbf\x14\xf5\x89\x23\x66\x5b\ 27 | \x85\x5e\x9b\xfa\x00\x45\x18\xf7\x17\x6e\x02\x4a\xc6\x48\xa0\x12\ 28 | \x9d\x6e\xbe\x7a\xb3\x76\x66\xf2\x20\x00\x10\x14\xfc\x70\x0e\x01\ 29 | \x00\x9e\x17\xa0\xe9\x87\x00\x80\xd2\xd4\xa5\xbe\xb9\x03\x01\x62\ 30 | \x41\xc8\x8a\x26\xc2\xa9\xcf\xe3\xa4\x6f\xae\x3a\x08\x40\x24\xe2\ 31 | \xd4\x97\x0f\xe3\xdc\x00\xb1\x68\x7f\x73\x92\x08\x5c\x9c\x5b\x07\ 32 | \x24\xbd\xb5\x09\x83\x74\xd0\x71\x06\x93\xf3\x4b\x45\x74\x19\x49\ 33 | \xd1\x51\x69\x1b\x16\x0b\x91\xe5\x0d\x75\xd1\xe6\xcc\xb6\x44\xbe\ 34 | \x83\xc7\x00\x7c\x00\x1e\x80\x06\x80\x46\xc1\xd0\x92\x58\x02\x28\ 35 | \x12\x3f\xe4\x09\x0a\x86\x96\x64\xb1\xa9\xf9\x00\xbe\x03\x18\xef\ 36 | \xec\x37\xeb\xe2\xee\xf2\xca\xc8\xb0\xb9\x78\xf5\xda\x65\xd3\x30\ 37 | \x74\x24\x04\x04\x51\x0c\x3f\x6a\x55\xdb\x6c\x06\xf0\x9a\xad\x31\ 38 | \x1d\x35\x0b\x18\x35\x8b\x50\xd4\x16\xd6\x75\x6b\xb4\xe9\xec\xb8\ 39 | \x20\x9a\x66\xb6\xe5\x00\x80\x96\x07\x8c\x5d\xb9\xf5\x36\xe2\x7c\ 40 | \xce\xf3\xfc\x8b\x23\x63\x23\x46\x23\xe0\xe0\xd2\x21\x73\x2e\xc0\ 41 | \x79\x9c\x76\x20\x70\xec\x45\xd0\x55\x05\x27\x27\x0d\x30\x67\xa7\ 42 | \x0e\xa2\x19\x66\x5b\xbf\x7a\x1e\x32\xb3\x2d\x41\x44\xf7\x6a\xb5\ 43 | \xfa\xfa\xe6\x46\x39\x34\x34\xa5\x7d\x89\xbb\xac\x51\xb3\x80\xc0\ 44 | \xf7\xc1\xd8\x76\x03\x44\x73\xcc\xb6\x7e\xca\xfb\x5d\xa7\x88\xd9\ 45 | \x96\x4f\x44\x25\xd7\x3d\xda\xdb\x2e\x57\x84\x2a\x01\x64\x96\x39\ 46 | \xa4\x23\x0a\x03\x38\xce\x76\x13\x44\x77\x98\x6d\x7d\xcb\x6b\xf5\ 47 | \x1c\x53\x66\x5b\x87\x20\x9a\xad\x56\xdd\xe3\xbd\xca\x3e\x81\x92\ 48 | \x0e\x42\xd1\xd0\x10\x45\x21\xca\xbf\x77\x7c\x10\x2d\x30\xdb\xfa\ 49 | \xdc\x4d\xe7\xcc\x7b\xc0\x6c\xcb\x01\x51\xa9\xba\x57\xf5\xf6\xab\ 50 | \x2e\x44\x2c\x40\x20\x68\x9a\x82\x98\x47\xd8\xdd\xaa\xf8\x20\x7a\ 51 | \xc0\x6c\x6b\xb5\x97\x86\x76\x86\xbe\x02\x40\xab\xad\xaf\xba\x17\ 52 | \x26\x67\xbe\x04\x7e\xb4\x48\xaa\x66\xe8\xaa\x0e\x1e\x85\x38\xa8\ 53 | \xec\xfa\x22\x0e\x1f\x39\xef\x9e\xbc\x4e\xe3\x69\x10\x80\x82\xd6\ 54 | \xe5\x33\x00\x14\x00\x14\x01\x14\xea\x1b\x1f\x77\xcd\x89\xeb\x8c\ 55 | \x47\xc9\x6d\x45\x33\x8c\xda\x41\xd5\x8f\x83\xfa\xd3\xf2\xfb\x67\ 56 | \x2f\xd3\x58\x3d\xd5\x52\xf3\x30\x19\xa0\x4a\xe2\x19\xc0\xc8\x20\ 57 | \x27\x6c\x6d\x73\x78\x62\x2a\xe4\x3c\x99\x8d\x9b\x47\xcf\xb7\x3e\ 58 | \x2c\xbf\x48\xf3\x33\x61\x35\x2d\xb0\xfd\xe3\xc8\x0d\x45\x06\xc8\ 59 | \x9b\x26\xfb\xe3\xd3\x0f\x6f\xec\x7f\x5d\xf9\x04\x40\x00\x88\x53\ 60 | \x93\xfd\x18\x00\x4f\x9f\x7f\xdd\x64\x35\x27\xdc\xab\x42\x4a\x2d\ 61 | \x49\xc5\x85\x04\xe1\xa9\x8f\x6e\x80\x3c\x4c\x16\x57\xa5\xf8\x3c\ 62 | \x20\x49\x8d\xf0\xaf\xd7\x1f\x96\x90\x53\x59\xef\x6c\x3f\xfa\x00\ 63 | \x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\ 64 | \x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\ 65 | \x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ 66 | \ 67 | \x00\x00\x03\x00\ 68 | \x89\ 69 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ 70 | \x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\ 71 | \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ 72 | \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x00\x48\x00\x00\ 73 | \x00\x48\x00\x46\xc9\x6b\x3e\x00\x00\x02\x7b\x49\x44\x41\x54\x48\ 74 | \xc7\xe5\x94\x4f\x4f\x13\x41\x18\xc6\x9f\x77\x76\xdb\xa6\xdb\x3f\ 75 | \x11\xb1\x20\x12\xea\xb4\x8a\xa0\x11\x53\x49\xe4\x4f\x8c\x09\x35\ 76 | \x1e\xcc\xea\xc9\xef\xe0\x77\x80\x2b\x97\x25\xf1\x3b\x78\x35\x31\ 77 | \x5e\x8c\x09\x31\x46\x0e\xe8\xd1\x9b\x62\x22\xd6\x03\x42\x62\x41\ 78 | \x6d\x29\xd0\x2e\x6d\x77\x67\xd7\x43\x17\x3a\x2c\x14\x8a\xe0\xc9\ 79 | \x37\x79\x32\x9b\x9d\x77\x7e\xcf\x3b\xef\x64\x06\xf8\xef\x82\xeb\ 80 | \xc6\xa3\xe3\xe4\xb3\xe3\xc1\x67\xa6\x01\xbc\xe0\x0f\x66\x9e\xb4\ 81 | \xbb\x46\x39\x46\xe5\x8f\xb5\x70\x70\x3a\x7b\x7b\x28\xf8\xf3\xf7\ 82 | \xc6\x4d\xed\xe2\x04\x4a\xb9\xb7\xef\x4f\xc5\x80\xeb\xc6\xc3\x80\ 83 | \xaa\x3c\xbd\x33\x72\x5d\xab\x83\x21\xd5\x97\x08\xe4\xd7\x0a\xe3\ 84 | \x91\x54\xb6\x54\xca\xcd\x7d\x38\x91\x01\xd7\x8d\x31\x62\xf4\x6a\ 85 | \x6c\x78\x50\xb3\x98\x8a\x95\x5f\x5b\xa8\xd5\x05\x06\x52\xdd\x81\ 86 | \x1f\xab\xc5\x6c\x2c\x9d\x5d\x2e\xe5\xe6\x3e\xfe\x95\x01\xd7\x8d\ 87 | \x01\x10\xbd\x1b\x1e\xba\x1c\x0d\x44\x22\x58\x2f\x5b\xb0\x6d\x81\ 88 | \xe2\xe6\x36\x98\xa2\x20\xd5\x97\x08\xac\xae\x15\xef\xc7\x2f\xdd\ 89 | \x5d\x28\xe5\xe6\x16\x0f\x62\xb0\x43\xe0\x3d\x20\x9a\xbf\xda\x9f\ 90 | \x8c\x6a\xf1\x18\x8a\x5b\xf5\xc6\x84\xdb\x18\xf2\x85\x0a\x36\xb7\ 91 | \x05\x32\x37\xfa\xc3\x8a\xc2\x9e\x71\xdd\xb8\xd7\xb6\x01\xd7\x8d\ 92 | \x38\x88\xe6\x53\x7d\xe7\x3b\xcf\x76\x75\xb2\x62\xb9\xbe\x3b\xe7\ 93 | \x4a\x79\xf9\x42\x05\x55\x1b\x18\xba\x96\x0e\x13\xa3\x97\x5c\x37\ 94 | \xc6\x8f\x34\xe0\xba\x11\x24\xa2\xd7\xdd\x89\x8e\xe4\x85\x64\x8f\ 95 | \x5a\xaa\xd4\xe1\xba\x68\x19\xf9\x42\x05\x82\x54\x0c\x5e\xe1\x1a\ 96 | \x88\xde\x70\xdd\xc8\xb4\x34\xe0\xba\x41\x20\x7a\x1e\x8b\x45\x32\ 97 | \x9c\xf7\x86\x1c\xc7\x41\x3c\xac\x42\x0b\xa9\x52\x56\xd3\xed\x4c\ 98 | \x34\x84\x64\x57\x0c\x41\x95\x41\xd3\xc2\x48\xa7\x7a\xa3\x20\x9a\ 99 | \xe7\xba\x31\xb0\x93\x43\x3e\x83\x69\x00\x93\xf2\x7f\xc6\x48\x1d\ 100 | \x19\xcd\x60\xc3\xb4\x00\x00\xa6\x59\x43\xc5\xac\x02\x00\x7a\xcf\ 101 | \x45\xb1\xf0\xe9\x0b\x84\xe3\xda\xbe\x8d\x2d\x03\x98\x58\x9a\x9d\ 102 | \x5a\x21\x1c\x11\x5c\x37\xdc\x5b\xa3\x19\x94\xab\x0d\x46\xc5\xac\ 103 | \xc2\x34\x6b\x00\x80\xee\x0e\x0d\x9f\x17\x16\xb1\x34\x3b\xd5\x92\ 104 | \xd3\xd6\x53\x21\x1c\xf9\x10\x9a\x2c\xcb\x76\x8e\x5c\xdb\x96\x81\ 105 | \x2d\x9a\x20\x92\xce\xc0\x12\xa7\x64\xb0\x67\x07\xd2\xa7\x65\x8b\ 106 | \x13\x19\x10\xbc\x9b\x2e\xa4\x4a\xe5\x66\x49\x3b\x68\xc9\x51\x5b\ 107 | \x40\x65\xc1\x71\x00\xe5\x00\x84\x10\xbb\x76\x1a\x00\xe1\xd3\x3e\ 108 | \x03\xe6\x01\x55\x49\x0a\x00\xd4\xac\x3a\x14\xbb\xe1\x60\x59\x16\ 109 | \x1c\xb1\xaf\x35\x21\x0f\x6a\x7b\x22\x6f\x3c\x70\x07\xb2\x18\x5c\ 110 | \x77\xfd\xfb\xd7\x6f\x81\x96\x8d\x74\x5d\xcb\x2b\xce\x91\xd6\xed\ 111 | \x01\xfa\xcf\x44\xae\x7e\x47\xcc\xd3\x0e\xc0\xf5\xe4\xf8\xda\x62\ 112 | \x03\xb0\xe4\x16\x1d\x76\xd1\x98\x0f\xce\xa4\x7c\xbf\x81\xe3\xe9\ 113 | \x90\x57\xeb\x1f\xc5\x1f\xb9\x7d\xe2\x03\xd0\xc7\x5f\x5c\x00\x00\ 114 | \x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\x77\ 115 | \x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\ 116 | \xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ 117 | \x00\x00\x03\x49\ 118 | \x89\ 119 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ 120 | \x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\ 121 | \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ 122 | \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x00\x48\x00\x00\ 123 | \x00\x48\x00\x46\xc9\x6b\x3e\x00\x00\x02\xc4\x49\x44\x41\x54\x48\ 124 | \xc7\xe5\x95\x4f\x48\x14\x71\x14\xc7\xbf\xbf\x99\x71\x77\x76\x75\ 125 | \x51\x52\xf1\xa0\x6c\xbb\x96\xab\x90\x61\x17\xf3\x50\xa7\xd0\x20\ 126 | \xb7\x20\xba\x05\x5d\x22\xe8\xd0\x21\xe8\x10\x2d\x48\x87\x0e\xb5\ 127 | \x86\x5d\x3a\x46\x9d\x3a\x25\x74\x30\x4a\x09\xed\x50\x87\xa0\x2e\ 128 | \x09\x19\x82\x90\xfb\x0f\xda\xd5\xdc\xdd\x69\xcd\x9d\x7f\x3b\xf3\ 129 | \xfb\x75\xd8\x1d\x19\xa7\x59\x57\x21\x4f\x3d\xf8\x32\x6f\x7e\xfc\ 130 | \xde\xfb\xbc\xf7\x7e\xbf\x61\x80\xff\xca\xc2\xe3\xf1\xdb\xfb\x8d\ 131 | \xe1\xf7\xb3\xb9\x2d\x32\x3a\x7f\x28\x32\x76\x7c\xa8\xfb\xf4\xab\ 132 | \x54\xea\x3d\xdd\x4b\x0c\xb7\xdf\x82\x3a\x3a\x5a\x2f\xa6\x5b\x7c\ 133 | \xef\xba\xce\x4e\x35\x1f\x04\x00\xc3\x27\xfa\xc5\x50\x4f\xe7\x88\ 134 | \xaf\xc9\xfc\x7c\xf4\xdc\x83\xce\x7f\x0e\xa0\x84\x60\x60\x20\x2c\ 135 | \x46\x8e\xf4\x44\x0c\x8e\x5b\x0c\x46\xe3\xbd\xbb\xed\x27\xf6\x97\ 136 | \x70\x74\x72\x85\x81\x05\xeb\x56\x43\x88\x38\x76\x66\x18\x39\x49\ 137 | \x45\x67\xab\x17\x6b\xd9\x0d\xba\xbc\x92\x29\x51\xce\x18\x4d\xbf\ 138 | \x9e\xf8\xd2\x10\x10\x3a\xff\x30\x7f\x6a\x64\xb0\xdd\x2b\x7a\xdc\ 139 | \xab\x21\x04\x8a\x4e\x21\xfd\xd6\xc0\x11\xa0\x3d\xe0\xc1\x2f\xa9\ 140 | \x84\x6f\xcb\x89\x2d\x46\xc9\xa5\xe4\xdc\x9d\x85\xc6\x23\x22\x3c\ 141 | \xf2\x25\x1d\x6b\x92\xfa\x97\x72\x45\x05\xd2\x6f\xad\x3a\x2a\x06\ 142 | \x6c\x6c\xea\xf0\x07\x02\x18\x1a\xec\x6b\xe1\x05\xcc\x04\xa3\x93\ 143 | \x57\x1a\x02\x28\xa5\xa0\x8c\x81\x31\xb8\xca\x69\x25\xd9\x00\xe7\ 144 | \x15\x31\x78\xac\xcf\x2f\x0a\xfc\x93\xde\x0b\x93\xb1\x5d\x01\x8a\ 145 | \xa6\x43\x56\x34\x28\x8a\x06\x59\x51\xb7\xfd\x6d\xa9\x1a\x54\x55\ 146 | \x83\xa2\xe8\x50\xd4\xaa\x8a\x9b\x2a\x4c\xf0\x18\xe8\xef\xf5\x53\ 147 | \xca\xee\x87\xc6\xe3\x97\xad\x7c\x82\x13\xa0\x2a\x3a\xca\xe5\x0a\ 148 | \x4c\xea\x52\x6e\x1d\xf3\x79\x05\xb0\x26\x20\x91\x48\xcb\x1c\xe3\ 149 | \x67\x12\x27\xe5\x69\xcc\xd5\x01\x18\xe6\x9e\x3e\xd0\x6d\x13\x3d\ 150 | \x3c\xfc\x1e\x0e\xab\xdf\xd3\x65\x45\xd3\x9f\xa7\xe6\x62\x37\xac\ 151 | \xe4\xae\x80\x8a\x49\x11\xf0\x7b\xc0\xe0\xb8\x62\x56\x87\xba\x01\ 152 | \x55\x37\x01\x00\x5e\x0f\x8f\x26\x9e\x20\xb1\x9a\x29\x1b\x86\xf1\ 153 | \x38\x35\x1b\x9b\x70\xee\xdf\x09\xa0\x4c\x4f\x27\x32\x26\xe0\x3e\ 154 | \x1e\xc2\x81\x04\x43\x21\x0e\x00\x3c\x02\x07\xc2\x28\x32\xc9\x1f\ 155 | \x65\xca\xd8\xbd\xe4\x6c\x6c\xca\x2d\x66\x07\x40\x60\xfe\xb0\xa1\ 156 | \x14\x09\xea\x18\x6b\xf6\x4a\x06\xa5\xa2\xc0\x73\x30\x0d\x03\xb9\ 157 | \x5c\x56\x06\x70\x2b\xf9\x26\xf6\xb4\x5e\x4c\xdd\x64\xa8\xde\x30\ 158 | \x4b\x04\x00\x39\x1c\x8d\x4b\x5d\xdd\x3d\xa2\x51\x31\x20\xfd\x5c\ 159 | \x97\x4d\xbd\x72\x3d\xb3\x70\xf7\x05\x00\x8a\x3a\x6d\x3b\xcf\x80\ 160 | \x77\x91\x0d\xc2\x88\xa6\xa8\x28\x15\x0a\xb2\x5e\xce\x5f\xcb\x7e\ 161 | \x78\xf4\x16\x80\x1f\x80\xe9\x10\x73\x03\x08\x36\xf1\x0e\xdf\x02\ 162 | \x61\xb3\x90\x57\x94\xe2\xea\xd5\xf5\x4f\xcf\x3e\x02\x10\x01\x18\ 163 | \xb5\xa4\x46\x4d\x1c\x80\x8a\x05\xb1\x00\xc4\x21\xce\xe1\x57\x21\ 164 | \x26\xcd\x6f\x65\x97\x6e\x6e\x7c\x9d\x5e\xac\xad\xb3\xda\x93\xda\ 165 | \xf6\xc3\xde\x81\xfd\x8f\xc6\x1c\xbe\x25\xab\xed\x0a\xef\x6b\x9b\ 166 | \x2f\x2c\xbd\x5c\xaa\x55\x58\xb1\x55\x6d\x75\xa1\xd7\xd6\x1b\x1e\ 167 | \x32\x71\xcc\xdf\x92\x65\xd4\x26\x73\xb7\x43\x3e\x70\xfb\x03\x13\ 168 | \x15\x53\xef\xce\x98\xcb\x53\x00\x00\x00\x19\x74\x45\x58\x74\x53\ 169 | \x6f\x66\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\ 170 | \x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\ 171 | \x49\x45\x4e\x44\xae\x42\x60\x82\ 172 | \x00\x00\x03\x45\ 173 | \x89\ 174 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ 175 | \x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\ 176 | \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ 177 | \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x00\x48\x00\x00\ 178 | \x00\x48\x00\x46\xc9\x6b\x3e\x00\x00\x02\xc0\x49\x44\x41\x54\x48\ 179 | \xc7\xe5\x54\x4d\x4f\x13\x41\x18\x7e\x66\xb6\xdd\x7e\x0b\x08\x45\ 180 | \x24\x15\x4a\x45\x01\x3d\x12\x0e\x9e\x30\x5e\xa4\x4a\xbc\xc8\xd9\ 181 | \x44\xff\x81\xf1\x42\x3d\x18\x43\xa2\x95\x0b\x89\x31\x1e\x3c\x18\ 182 | \xae\x26\x5c\xb1\x9a\x60\x8c\xc0\xc9\x04\xe3\xc5\x84\x04\x93\x52\ 183 | \x40\xa2\x05\x1a\xbe\xf6\xab\xdd\xee\x8c\x87\x4e\xa1\x85\x6e\x59\ 184 | \x2e\x5e\x9c\xe4\xd9\xc9\x64\x9e\xf7\x7d\xde\x8f\x79\x17\xf8\x2f\ 185 | \x57\xf4\x56\xf2\xa1\x53\x2e\x3d\x9d\x6b\x4e\xa2\xc3\xe3\x2f\x01\ 186 | \x4c\x38\xb5\x70\x39\x25\xf6\xf7\xbf\x71\xe7\xda\xc6\xdf\x85\x9b\ 187 | \xce\x0c\x6d\xe6\x76\x1d\x87\xe4\x28\x83\xab\xd7\x9f\x06\x73\x6d\ 188 | \xdb\x5f\x22\x6d\xcd\xf1\x6b\x03\x7d\xfe\xd3\xe4\x7c\x62\x06\x5d\ 189 | \x77\x9e\x9d\xd3\x2c\x69\xae\x2b\x12\x8e\xf6\xf6\x75\xc9\x9c\x4a\ 190 | \xa7\xf1\x0f\x52\xef\xf2\xe2\xcd\x64\x37\x73\x91\xf9\xee\x58\x7b\ 191 | \xf8\x42\x47\xbb\xb4\xb9\x63\x20\xd2\x1a\xc4\xcc\xa7\xaf\x28\x32\ 192 | \x66\xd8\x1a\x72\xa2\x65\x52\xa3\xcd\x75\x33\x88\x0d\x25\x07\x18\ 193 | \x25\x33\xbd\x97\x3b\x42\x2d\xad\x2d\x34\xbb\xa3\x83\x31\x60\x7b\ 194 | \x3f\x8f\xc1\xc1\x7e\x80\xc3\x5b\x2b\x5c\xcb\x62\x98\x9d\xfd\x76\ 195 | \xe0\xb7\xa6\x40\x67\xfc\x45\x9c\x4b\x64\xea\x4a\x4f\x34\x10\x6a\ 196 | \x6c\x40\x6e\x3f\x0f\xce\x4b\x77\xbb\x6a\x01\x7b\x9a\x4d\x39\x08\ 197 | \xd0\xda\x50\xad\x7b\x4c\x20\x76\x3b\xf9\x80\x52\xf2\xaa\xb7\x27\ 198 | \xe6\xf7\x06\xfc\xd8\xd5\xcc\xe3\x15\xe0\x36\x95\xe1\x40\xd1\xe2\ 199 | \xf6\x02\x9d\xf1\xe4\x0d\xc6\xf9\xdb\x9e\x4b\x51\x10\xb7\x8c\xdc\ 200 | \x9e\x81\x03\x3a\x07\x08\xe1\xa5\x73\x85\x0f\x2e\xbe\x04\x04\xe0\ 201 | \x40\xc1\x5b\xfd\x30\xab\x4e\x2b\x1f\x12\x9f\x09\xa5\x13\xcb\x99\ 202 | \x35\xcd\x32\x4d\x58\xa6\x09\x45\xd1\x4b\x50\x75\xec\x2b\x06\x14\ 203 | \xc5\x80\xa2\x1e\x42\x55\x0d\xa8\x6a\xbe\x74\xd6\x0c\xe8\x7a\xc1\ 204 | \x5e\x00\x00\x96\xa7\x47\x1f\x19\xf9\xe2\xf8\xd2\xcf\x65\x4d\x96\ 205 | \x00\xbf\xc7\xf1\x2c\x02\x00\x8a\x16\xab\x2f\x00\x00\x99\xf7\xa3\ 206 | \x63\x45\x93\x25\xd2\xe9\x15\x8d\x80\xc1\x2b\x1f\xbe\x7d\x8f\x2c\ 207 | \xa1\x31\xe8\x41\x53\xe8\x38\xce\x86\xbc\x30\x8b\xd5\x02\x75\xe7\ 208 | \x20\x36\xfc\xfc\x3e\x87\xf4\xfa\x7c\xa4\xdd\x47\xa8\x0b\x79\xd3\ 209 | \x42\xd0\xe7\xc6\x4a\x3a\xcd\x38\x03\xaf\x63\xca\x32\xa9\x84\x6c\ 210 | \x9b\x41\x79\xa5\xa7\x1f\x4f\x5a\x16\xee\xfd\xfe\xb5\xae\x17\x0a\ 211 | \x79\xb8\x24\x0a\xd3\x62\xe0\x0c\x14\x5a\x3e\x68\x07\x17\x0f\x84\ 212 | \x1c\x65\x50\xe6\x74\x0c\x8d\xc5\x29\x95\xa7\x1a\x5b\xc2\x7e\x8f\ 213 | \xcf\x8b\xec\xda\x2a\x32\xa9\x84\x4f\xdc\x73\x01\x26\x76\xeb\xc4\ 214 | \x1e\x08\x61\x17\x00\x0f\x00\xdf\xea\xc7\x27\xf3\xa6\xb2\x31\xb2\ 215 | \x93\xdb\xd0\x74\xed\x60\xca\x82\x15\x08\x08\xf8\x04\x64\x00\x92\ 216 | \x9d\x40\xd9\xb9\x5b\x10\x3d\x00\x3c\xeb\x73\x13\xdf\x8d\xec\xd2\ 217 | \xc8\x5e\x6e\x4b\x11\xbc\x90\x70\xee\x17\x4e\xbd\x65\xae\xb0\x73\ 218 | \xd7\x13\x38\x0a\x0a\x80\xfc\x59\x98\xfc\xa1\xae\x2f\xdc\xe5\x9c\ 219 | \x6d\x89\x20\x24\x01\x5a\xc9\xab\x00\x6a\xfd\x7b\xcb\x35\xc5\x91\ 220 | \x9d\x03\xb0\xb4\xec\xe2\x86\xe4\x6f\x4a\xe9\xd9\xc5\x4d\x00\x05\ 221 | \x00\x26\x80\x62\x0d\x98\x00\xb8\x93\x26\xd3\x8a\x28\xcb\x20\x47\ 222 | \x84\x99\x68\x2e\xab\x68\xf6\xbf\x59\x7f\x01\x28\xbf\x2c\x4d\x9c\ 223 | \x91\xce\xbe\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\ 224 | \x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\ 225 | \x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\ 226 | \xae\x42\x60\x82\ 227 | " 228 | 229 | qt_resource_name = "\ 230 | \x00\x05\ 231 | \x00\x68\x99\x67\ 232 | \x00\x61\ 233 | \x00\x72\x00\x72\x00\x6f\x00\x77\ 234 | \x00\x05\ 235 | \x00\x6f\xa6\x53\ 236 | \x00\x69\ 237 | \x00\x63\x00\x6f\x00\x6e\x00\x73\ 238 | \x00\x08\ 239 | \x06\xe1\x5a\x27\ 240 | \x00\x64\ 241 | \x00\x6f\x00\x77\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ 242 | \x00\x06\ 243 | \x07\xc3\x57\x47\ 244 | \x00\x75\ 245 | \x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\ 246 | \x00\x09\ 247 | \x0d\xf7\xa6\xa7\ 248 | \x00\x72\ 249 | \x00\x69\x00\x67\x00\x68\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ 250 | \x00\x08\ 251 | \x0b\xd7\x59\x07\ 252 | \x00\x6c\ 253 | \x00\x65\x00\x66\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ 254 | " 255 | 256 | qt_resource_struct = "\ 257 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 258 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 259 | \x00\x00\x00\x10\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\ 260 | \x00\x00\x00\x20\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 261 | \x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x03\x35\ 262 | \x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\x09\x86\ 263 | \x00\x00\x00\x48\x00\x00\x00\x00\x00\x01\x00\x00\x06\x39\ 264 | " 265 | 266 | def qInitResources(): 267 | QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 268 | 269 | def qCleanupResources(): 270 | QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 271 | 272 | qInitResources() 273 | -------------------------------------------------------------------------------- /main/python/ui/laser_control.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LaserControl 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 320 13 | 415 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 320 25 | 415 26 | 27 | 28 | 29 | 30 | 367 31 | 480 32 | 33 | 34 | 35 | Qt::DefaultContextMenu 36 | 37 | 38 | false 39 | 40 | 41 | Laser Control 42 | 43 | 44 | false 45 | 46 | 47 | QTabWidget::Rounded 48 | 49 | 50 | 51 | 52 | 53 | 10 54 | 20 55 | 111 56 | 101 57 | 58 | 59 | 60 | 61 | 62 | 63 | Move 64 | 65 | 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | :/arrow/icons/left.png:/arrow/icons/left.png 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | 89 | Qt::LeftToRight 90 | 91 | 92 | 93 | 94 | 95 | 96 | :/arrow/icons/down.png:/arrow/icons/down.png 97 | 98 | 99 | false 100 | 101 | 102 | false 103 | 104 | 105 | 106 | 107 | 108 | 109 | Qt::LeftToRight 110 | 111 | 112 | 113 | 114 | 115 | 116 | :/arrow/icons/up.png:/arrow/icons/up.png 117 | 118 | 119 | false 120 | 121 | 122 | false 123 | 124 | 125 | 126 | 127 | 128 | 129 | Qt::LeftToRight 130 | 131 | 132 | 133 | 134 | 135 | 136 | :/arrow/icons/right.png:/arrow/icons/right.png 137 | 138 | 139 | false 140 | 141 | 142 | false 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 130 152 | 20 153 | 191 154 | 101 155 | 156 | 157 | 158 | Current position 159 | 160 | 161 | 162 | 163 | 164 | false 165 | 166 | 167 | 168 | 169 | 170 | 171 | Vertical 172 | 173 | 174 | 175 | 176 | 177 | 178 | Horizontal 179 | 180 | 181 | 182 | 183 | 184 | 185 | Qt::Vertical 186 | 187 | 188 | 189 | 20 190 | 40 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | false 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 10 211 | 130 212 | 141 213 | 51 214 | 215 | 216 | 217 | false 218 | 219 | 220 | Laser 221 | 222 | 223 | false 224 | 225 | 226 | 227 | 228 | 10 229 | 20 230 | 50 231 | 22 232 | 233 | 234 | 235 | On 236 | 237 | 238 | 239 | 240 | 241 | 70 242 | 20 243 | 54 244 | 22 245 | 246 | 247 | 248 | Off 249 | 250 | 251 | true 252 | 253 | 254 | 255 | 256 | 257 | 258 | 140 259 | 130 260 | 171 261 | 81 262 | 263 | 264 | 265 | Status 266 | 267 | 268 | 269 | 270 | 10 271 | 20 272 | 161 273 | 31 274 | 275 | 276 | 277 | QFrame::StyledPanel 278 | 279 | 280 | QFrame::Raised 281 | 282 | 283 | 284 | 285 | 0 286 | 0 287 | 161 288 | 31 289 | 290 | 291 | 292 | 293 | 75 294 | true 295 | 296 | 297 | 298 | true 299 | 300 | 301 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 302 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 303 | p, li { white-space: pre-wrap; } 304 | </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:600; font-style:normal;"> 305 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">References: 0/2</p></body></html> 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 10 314 | 120 315 | 301 316 | 20 317 | 318 | 319 | 320 | Qt::Horizontal 321 | 322 | 323 | 324 | 325 | true 326 | 327 | 328 | 329 | 10 330 | 210 331 | 301 332 | 171 333 | 334 | 335 | 336 | 1 337 | 338 | 339 | 340 | Configuration 341 | 342 | 343 | 344 | 345 | 10 346 | 10 347 | 170 348 | 22 349 | 350 | 351 | 352 | 353 | 75 354 | true 355 | 356 | 357 | 358 | Configuration mode 359 | 360 | 361 | 362 | 363 | true 364 | 365 | 366 | 367 | 0 368 | 40 369 | 301 370 | 81 371 | 372 | 373 | 374 | 375 | 376 | 377 | true 378 | 379 | 380 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 381 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 382 | p, li { white-space: pre-wrap; } 383 | </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> 384 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">With this option activated, the &quot;goto&quot;</span></p> 385 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">commands received from Stellarium will</span></p> 386 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">be send to the device in order to sets in order</span></p> 387 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">each one of the </span><span style=" font-weight:600; font-style:italic;">two reference objects</span><span style=" font-style:italic;">.</span></p></body></html> 388 | 389 | 390 | 391 | 392 | 393 | 10 394 | 20 395 | 281 396 | 101 397 | 398 | 399 | 400 | Redefine reference object: 401 | 402 | 403 | true 404 | 405 | 406 | false 407 | 408 | 409 | 410 | 411 | 50 412 | 30 413 | 191 414 | 22 415 | 416 | 417 | 418 | Reference object 1 419 | 420 | 421 | true 422 | 423 | 424 | true 425 | 426 | 427 | false 428 | 429 | 430 | 431 | 432 | 433 | 50 434 | 50 435 | 191 436 | 22 437 | 438 | 439 | 440 | Reference object 2 441 | 442 | 443 | true 444 | 445 | 446 | 447 | 448 | 449 | 50 450 | 70 451 | 191 452 | 22 453 | 454 | 455 | 456 | Reference object 3 457 | 458 | 459 | true 460 | 461 | 462 | 463 | 464 | 465 | 466 | Pointing 467 | 468 | 469 | 470 | 471 | 10 472 | 110 473 | 260 474 | 22 475 | 476 | 477 | 478 | 479 | 75 480 | true 481 | 482 | 483 | 484 | Tracking mode 485 | 486 | 487 | 488 | 489 | 490 | 0 491 | 10 492 | 291 493 | 81 494 | 495 | 496 | 497 | true 498 | 499 | 500 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 501 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 502 | p, li { white-space: pre-wrap; } 503 | </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> 504 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Now you can start to use the device.</span></p> 505 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;"><br /></p> 506 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">The laser will points to the equatorial</span></p> 507 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">coordinates received from Stellarium.</span></p></body></html> 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 10 516 | 190 517 | 301 518 | 20 519 | 520 | 521 | 522 | Qt::Horizontal 523 | 524 | 525 | 526 | 527 | 528 | 529 | 0 530 | 0 531 | 320 532 | 25 533 | 534 | 535 | 536 | 537 | &File 538 | 539 | 540 | 541 | 542 | 543 | &Device 544 | 545 | 546 | 547 | &Connect 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | &Help 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | &Quit 570 | 571 | 572 | 573 | 574 | &Iniciar 575 | 576 | 577 | 578 | 579 | false 580 | 581 | 582 | &Detener 583 | 584 | 585 | 586 | 587 | false 588 | 589 | 590 | &Desconectar 591 | 592 | 593 | 594 | 595 | About .. 596 | 597 | 598 | 599 | 600 | Cargar 601 | 602 | 603 | 604 | 605 | Guardar en archivo 606 | 607 | 608 | 609 | 610 | Cargar desde archivo 611 | 612 | 613 | 614 | 615 | false 616 | 617 | 618 | Recalibrar 619 | 620 | 621 | 622 | 623 | Refresh Port List 624 | 625 | 626 | 627 | 628 | upButton 629 | rightButton 630 | downButton 631 | leftButton 632 | posHorizontal 633 | posVertical 634 | laserOn 635 | laserOff 636 | tabWidget 637 | confMode 638 | textEdit 639 | textEdit_2 640 | trackMode 641 | text_status 642 | 643 | 644 | 645 | 646 | 647 | 648 | action_Cerrar 649 | triggered() 650 | LaserControl 651 | close() 652 | 653 | 654 | -1 655 | -1 656 | 657 | 658 | 183 659 | 239 660 | 661 | 662 | 663 | 664 | 665 | -------------------------------------------------------------------------------- /main/python/ui/laser_control_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file './ui/laser_control.ui' 4 | # 5 | # Created: Sun Mar 31 15:52:08 2013 6 | # by: PyQt4 UI code generator 4.9.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt4 import QtCore, QtGui 11 | 12 | try: 13 | _fromUtf8 = QtCore.QString.fromUtf8 14 | except AttributeError: 15 | _fromUtf8 = lambda s: s 16 | 17 | class Ui_LaserControl(object): 18 | def setupUi(self, LaserControl): 19 | LaserControl.setObjectName(_fromUtf8("LaserControl")) 20 | LaserControl.setWindowModality(QtCore.Qt.NonModal) 21 | LaserControl.resize(320, 415) 22 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) 23 | sizePolicy.setHorizontalStretch(0) 24 | sizePolicy.setVerticalStretch(0) 25 | sizePolicy.setHeightForWidth(LaserControl.sizePolicy().hasHeightForWidth()) 26 | LaserControl.setSizePolicy(sizePolicy) 27 | LaserControl.setMinimumSize(QtCore.QSize(320, 415)) 28 | LaserControl.setMaximumSize(QtCore.QSize(367, 480)) 29 | LaserControl.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) 30 | LaserControl.setAcceptDrops(False) 31 | LaserControl.setDocumentMode(False) 32 | LaserControl.setTabShape(QtGui.QTabWidget.Rounded) 33 | self.centralwidget = QtGui.QWidget(LaserControl) 34 | self.centralwidget.setObjectName(_fromUtf8("centralwidget")) 35 | self.Movements = QtGui.QGroupBox(self.centralwidget) 36 | self.Movements.setGeometry(QtCore.QRect(10, 20, 111, 101)) 37 | self.Movements.setStyleSheet(_fromUtf8("")) 38 | self.Movements.setFlat(True) 39 | self.Movements.setObjectName(_fromUtf8("Movements")) 40 | self.gridLayout = QtGui.QGridLayout(self.Movements) 41 | self.gridLayout.setObjectName(_fromUtf8("gridLayout")) 42 | self.leftButton = QtGui.QPushButton(self.Movements) 43 | self.leftButton.setText(_fromUtf8("")) 44 | icon = QtGui.QIcon() 45 | icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/arrow/icons/left.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) 46 | self.leftButton.setIcon(icon) 47 | self.leftButton.setCheckable(False) 48 | self.leftButton.setAutoRepeat(False) 49 | self.leftButton.setObjectName(_fromUtf8("leftButton")) 50 | self.gridLayout.addWidget(self.leftButton, 1, 0, 1, 1) 51 | self.downButton = QtGui.QPushButton(self.Movements) 52 | self.downButton.setLayoutDirection(QtCore.Qt.LeftToRight) 53 | self.downButton.setText(_fromUtf8("")) 54 | icon1 = QtGui.QIcon() 55 | icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/arrow/icons/down.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) 56 | self.downButton.setIcon(icon1) 57 | self.downButton.setCheckable(False) 58 | self.downButton.setAutoRepeat(False) 59 | self.downButton.setObjectName(_fromUtf8("downButton")) 60 | self.gridLayout.addWidget(self.downButton, 2, 1, 1, 1) 61 | self.upButton = QtGui.QPushButton(self.Movements) 62 | self.upButton.setLayoutDirection(QtCore.Qt.LeftToRight) 63 | self.upButton.setText(_fromUtf8("")) 64 | icon2 = QtGui.QIcon() 65 | icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/arrow/icons/up.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) 66 | self.upButton.setIcon(icon2) 67 | self.upButton.setCheckable(False) 68 | self.upButton.setAutoRepeat(False) 69 | self.upButton.setObjectName(_fromUtf8("upButton")) 70 | self.gridLayout.addWidget(self.upButton, 0, 1, 1, 1) 71 | self.rightButton = QtGui.QPushButton(self.Movements) 72 | self.rightButton.setLayoutDirection(QtCore.Qt.LeftToRight) 73 | self.rightButton.setText(_fromUtf8("")) 74 | icon3 = QtGui.QIcon() 75 | icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/arrow/icons/right.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) 76 | self.rightButton.setIcon(icon3) 77 | self.rightButton.setCheckable(False) 78 | self.rightButton.setAutoRepeat(False) 79 | self.rightButton.setObjectName(_fromUtf8("rightButton")) 80 | self.gridLayout.addWidget(self.rightButton, 1, 2, 1, 1) 81 | self.Positioning = QtGui.QGroupBox(self.centralwidget) 82 | self.Positioning.setGeometry(QtCore.QRect(130, 20, 191, 101)) 83 | self.Positioning.setObjectName(_fromUtf8("Positioning")) 84 | self.gridLayout_2 = QtGui.QGridLayout(self.Positioning) 85 | self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) 86 | self.posVertical = QtGui.QLineEdit(self.Positioning) 87 | self.posVertical.setReadOnly(False) 88 | self.posVertical.setObjectName(_fromUtf8("posVertical")) 89 | self.gridLayout_2.addWidget(self.posVertical, 2, 1, 1, 1) 90 | self.label_Vertical = QtGui.QLabel(self.Positioning) 91 | self.label_Vertical.setObjectName(_fromUtf8("label_Vertical")) 92 | self.gridLayout_2.addWidget(self.label_Vertical, 2, 0, 1, 1) 93 | self.label_Horizontal = QtGui.QLabel(self.Positioning) 94 | self.label_Horizontal.setObjectName(_fromUtf8("label_Horizontal")) 95 | self.gridLayout_2.addWidget(self.label_Horizontal, 1, 0, 1, 1) 96 | spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) 97 | self.gridLayout_2.addItem(spacerItem, 4, 0, 1, 1) 98 | self.posHorizontal = QtGui.QLineEdit(self.Positioning) 99 | self.posHorizontal.setText(_fromUtf8("")) 100 | self.posHorizontal.setReadOnly(False) 101 | self.posHorizontal.setObjectName(_fromUtf8("posHorizontal")) 102 | self.gridLayout_2.addWidget(self.posHorizontal, 1, 1, 1, 1) 103 | self.Laser = QtGui.QGroupBox(self.centralwidget) 104 | self.Laser.setGeometry(QtCore.QRect(10, 130, 141, 51)) 105 | self.Laser.setAutoFillBackground(False) 106 | self.Laser.setCheckable(False) 107 | self.Laser.setObjectName(_fromUtf8("Laser")) 108 | self.laserOn = QtGui.QRadioButton(self.Laser) 109 | self.laserOn.setGeometry(QtCore.QRect(10, 20, 50, 22)) 110 | self.laserOn.setObjectName(_fromUtf8("laserOn")) 111 | self.laserOff = QtGui.QRadioButton(self.Laser) 112 | self.laserOff.setGeometry(QtCore.QRect(70, 20, 54, 22)) 113 | self.laserOff.setChecked(True) 114 | self.laserOff.setObjectName(_fromUtf8("laserOff")) 115 | self.Status = QtGui.QGroupBox(self.centralwidget) 116 | self.Status.setGeometry(QtCore.QRect(140, 130, 171, 81)) 117 | self.Status.setObjectName(_fromUtf8("Status")) 118 | self.status_frame = QtGui.QFrame(self.Status) 119 | self.status_frame.setGeometry(QtCore.QRect(10, 20, 161, 31)) 120 | self.status_frame.setFrameShape(QtGui.QFrame.StyledPanel) 121 | self.status_frame.setFrameShadow(QtGui.QFrame.Raised) 122 | self.status_frame.setObjectName(_fromUtf8("status_frame")) 123 | self.text_status = QtGui.QTextEdit(self.status_frame) 124 | self.text_status.setGeometry(QtCore.QRect(0, 0, 161, 31)) 125 | font = QtGui.QFont() 126 | font.setBold(True) 127 | font.setWeight(75) 128 | self.text_status.setFont(font) 129 | self.text_status.setReadOnly(True) 130 | self.text_status.setObjectName(_fromUtf8("text_status")) 131 | self.line_2 = QtGui.QFrame(self.centralwidget) 132 | self.line_2.setGeometry(QtCore.QRect(10, 120, 301, 20)) 133 | self.line_2.setFrameShape(QtGui.QFrame.HLine) 134 | self.line_2.setFrameShadow(QtGui.QFrame.Sunken) 135 | self.line_2.setObjectName(_fromUtf8("line_2")) 136 | self.tabWidget = QtGui.QTabWidget(self.centralwidget) 137 | self.tabWidget.setEnabled(True) 138 | self.tabWidget.setGeometry(QtCore.QRect(10, 210, 301, 171)) 139 | self.tabWidget.setObjectName(_fromUtf8("tabWidget")) 140 | self.tab_config = QtGui.QWidget() 141 | self.tab_config.setObjectName(_fromUtf8("tab_config")) 142 | self.confMode = QtGui.QCheckBox(self.tab_config) 143 | self.confMode.setGeometry(QtCore.QRect(10, 10, 170, 22)) 144 | font = QtGui.QFont() 145 | font.setBold(True) 146 | font.setWeight(75) 147 | self.confMode.setFont(font) 148 | self.confMode.setObjectName(_fromUtf8("confMode")) 149 | self.textEdit = QtGui.QTextEdit(self.tab_config) 150 | self.textEdit.setEnabled(True) 151 | self.textEdit.setGeometry(QtCore.QRect(0, 40, 301, 81)) 152 | self.textEdit.setToolTip(_fromUtf8("")) 153 | self.textEdit.setReadOnly(True) 154 | self.textEdit.setObjectName(_fromUtf8("textEdit")) 155 | self.Reconfigure = QtGui.QGroupBox(self.tab_config) 156 | self.Reconfigure.setGeometry(QtCore.QRect(10, 20, 281, 101)) 157 | self.Reconfigure.setCheckable(True) 158 | self.Reconfigure.setChecked(False) 159 | self.Reconfigure.setObjectName(_fromUtf8("Reconfigure")) 160 | self.redef_1 = QtGui.QCheckBox(self.Reconfigure) 161 | self.redef_1.setGeometry(QtCore.QRect(50, 30, 191, 22)) 162 | self.redef_1.setCheckable(True) 163 | self.redef_1.setAutoExclusive(True) 164 | self.redef_1.setTristate(False) 165 | self.redef_1.setObjectName(_fromUtf8("redef_1")) 166 | self.redef_2 = QtGui.QCheckBox(self.Reconfigure) 167 | self.redef_2.setGeometry(QtCore.QRect(50, 50, 191, 22)) 168 | self.redef_2.setAutoExclusive(True) 169 | self.redef_2.setObjectName(_fromUtf8("redef_2")) 170 | self.redef_3 = QtGui.QCheckBox(self.Reconfigure) 171 | self.redef_3.setGeometry(QtCore.QRect(50, 70, 191, 22)) 172 | self.redef_3.setAutoExclusive(True) 173 | self.redef_3.setObjectName(_fromUtf8("redef_3")) 174 | self.tabWidget.addTab(self.tab_config, _fromUtf8("")) 175 | self.tab_move = QtGui.QWidget() 176 | self.tab_move.setObjectName(_fromUtf8("tab_move")) 177 | self.trackMode = QtGui.QCheckBox(self.tab_move) 178 | self.trackMode.setGeometry(QtCore.QRect(10, 110, 260, 22)) 179 | font = QtGui.QFont() 180 | font.setBold(True) 181 | font.setWeight(75) 182 | self.trackMode.setFont(font) 183 | self.trackMode.setObjectName(_fromUtf8("trackMode")) 184 | self.textEdit_2 = QtGui.QTextEdit(self.tab_move) 185 | self.textEdit_2.setGeometry(QtCore.QRect(0, 10, 291, 81)) 186 | self.textEdit_2.setReadOnly(True) 187 | self.textEdit_2.setObjectName(_fromUtf8("textEdit_2")) 188 | self.tabWidget.addTab(self.tab_move, _fromUtf8("")) 189 | self.line_3 = QtGui.QFrame(self.centralwidget) 190 | self.line_3.setGeometry(QtCore.QRect(10, 190, 301, 20)) 191 | self.line_3.setFrameShape(QtGui.QFrame.HLine) 192 | self.line_3.setFrameShadow(QtGui.QFrame.Sunken) 193 | self.line_3.setObjectName(_fromUtf8("line_3")) 194 | LaserControl.setCentralWidget(self.centralwidget) 195 | self.menubar = QtGui.QMenuBar(LaserControl) 196 | self.menubar.setGeometry(QtCore.QRect(0, 0, 320, 25)) 197 | self.menubar.setObjectName(_fromUtf8("menubar")) 198 | self.menu_Archivo = QtGui.QMenu(self.menubar) 199 | self.menu_Archivo.setObjectName(_fromUtf8("menu_Archivo")) 200 | self.menu_Dispositivo = QtGui.QMenu(self.menubar) 201 | self.menu_Dispositivo.setObjectName(_fromUtf8("menu_Dispositivo")) 202 | self.menu_Connect = QtGui.QMenu(self.menu_Dispositivo) 203 | self.menu_Connect.setObjectName(_fromUtf8("menu_Connect")) 204 | self.menu_Ayuda = QtGui.QMenu(self.menubar) 205 | self.menu_Ayuda.setObjectName(_fromUtf8("menu_Ayuda")) 206 | LaserControl.setMenuBar(self.menubar) 207 | self.action_Cerrar = QtGui.QAction(LaserControl) 208 | self.action_Cerrar.setObjectName(_fromUtf8("action_Cerrar")) 209 | self.action_Iniciar = QtGui.QAction(LaserControl) 210 | self.action_Iniciar.setObjectName(_fromUtf8("action_Iniciar")) 211 | self.action_Detener = QtGui.QAction(LaserControl) 212 | self.action_Detener.setEnabled(False) 213 | self.action_Detener.setObjectName(_fromUtf8("action_Detener")) 214 | self.action_Desconectar = QtGui.QAction(LaserControl) 215 | self.action_Desconectar.setEnabled(False) 216 | self.action_Desconectar.setObjectName(_fromUtf8("action_Desconectar")) 217 | self.actionAcerca_de = QtGui.QAction(LaserControl) 218 | self.actionAcerca_de.setObjectName(_fromUtf8("actionAcerca_de")) 219 | self.actionCargar = QtGui.QAction(LaserControl) 220 | self.actionCargar.setObjectName(_fromUtf8("actionCargar")) 221 | self.actionGuardar_configuraci_n = QtGui.QAction(LaserControl) 222 | self.actionGuardar_configuraci_n.setObjectName(_fromUtf8("actionGuardar_configuraci_n")) 223 | self.actionCargar_configuraci_n = QtGui.QAction(LaserControl) 224 | self.actionCargar_configuraci_n.setObjectName(_fromUtf8("actionCargar_configuraci_n")) 225 | self.action_Recalibrar = QtGui.QAction(LaserControl) 226 | self.action_Recalibrar.setEnabled(False) 227 | self.action_Recalibrar.setObjectName(_fromUtf8("action_Recalibrar")) 228 | self.action_Refresh = QtGui.QAction(LaserControl) 229 | self.action_Refresh.setObjectName(_fromUtf8("action_Refresh")) 230 | self.menu_Archivo.addAction(self.action_Cerrar) 231 | self.menu_Connect.addAction(self.action_Refresh) 232 | self.menu_Connect.addSeparator() 233 | self.menu_Dispositivo.addAction(self.menu_Connect.menuAction()) 234 | self.menu_Dispositivo.addAction(self.action_Desconectar) 235 | self.menu_Dispositivo.addSeparator() 236 | self.menu_Dispositivo.addAction(self.action_Recalibrar) 237 | self.menu_Ayuda.addAction(self.actionAcerca_de) 238 | self.menubar.addAction(self.menu_Archivo.menuAction()) 239 | self.menubar.addAction(self.menu_Dispositivo.menuAction()) 240 | self.menubar.addAction(self.menu_Ayuda.menuAction()) 241 | 242 | self.retranslateUi(LaserControl) 243 | self.tabWidget.setCurrentIndex(1) 244 | QtCore.QObject.connect(self.action_Cerrar, QtCore.SIGNAL(_fromUtf8("triggered()")), LaserControl.close) 245 | QtCore.QMetaObject.connectSlotsByName(LaserControl) 246 | LaserControl.setTabOrder(self.upButton, self.rightButton) 247 | LaserControl.setTabOrder(self.rightButton, self.downButton) 248 | LaserControl.setTabOrder(self.downButton, self.leftButton) 249 | LaserControl.setTabOrder(self.leftButton, self.posHorizontal) 250 | LaserControl.setTabOrder(self.posHorizontal, self.posVertical) 251 | LaserControl.setTabOrder(self.posVertical, self.laserOn) 252 | LaserControl.setTabOrder(self.laserOn, self.laserOff) 253 | LaserControl.setTabOrder(self.laserOff, self.tabWidget) 254 | LaserControl.setTabOrder(self.tabWidget, self.confMode) 255 | LaserControl.setTabOrder(self.confMode, self.textEdit) 256 | LaserControl.setTabOrder(self.textEdit, self.textEdit_2) 257 | LaserControl.setTabOrder(self.textEdit_2, self.trackMode) 258 | LaserControl.setTabOrder(self.trackMode, self.text_status) 259 | 260 | def retranslateUi(self, LaserControl): 261 | LaserControl.setWindowTitle(QtGui.QApplication.translate("LaserControl", "Laser Control", None, QtGui.QApplication.UnicodeUTF8)) 262 | self.Movements.setTitle(QtGui.QApplication.translate("LaserControl", "Move", None, QtGui.QApplication.UnicodeUTF8)) 263 | self.Positioning.setTitle(QtGui.QApplication.translate("LaserControl", "Current position", None, QtGui.QApplication.UnicodeUTF8)) 264 | self.label_Vertical.setText(QtGui.QApplication.translate("LaserControl", "Vertical", None, QtGui.QApplication.UnicodeUTF8)) 265 | self.label_Horizontal.setText(QtGui.QApplication.translate("LaserControl", "Horizontal", None, QtGui.QApplication.UnicodeUTF8)) 266 | self.Laser.setTitle(QtGui.QApplication.translate("LaserControl", "Laser", None, QtGui.QApplication.UnicodeUTF8)) 267 | self.laserOn.setText(QtGui.QApplication.translate("LaserControl", "On", None, QtGui.QApplication.UnicodeUTF8)) 268 | self.laserOff.setText(QtGui.QApplication.translate("LaserControl", "Off", None, QtGui.QApplication.UnicodeUTF8)) 269 | self.Status.setTitle(QtGui.QApplication.translate("LaserControl", "Status", None, QtGui.QApplication.UnicodeUTF8)) 270 | self.text_status.setHtml(QtGui.QApplication.translate("LaserControl", "\n" 271 | "\n" 274 | "

References: 0/2

", None, QtGui.QApplication.UnicodeUTF8)) 275 | self.confMode.setText(QtGui.QApplication.translate("LaserControl", "Configuration mode", None, QtGui.QApplication.UnicodeUTF8)) 276 | self.textEdit.setHtml(QtGui.QApplication.translate("LaserControl", "\n" 277 | "\n" 280 | "

With this option activated, the "goto"

\n" 281 | "

commands received from Stellarium will

\n" 282 | "

be send to the device in order to sets in order

\n" 283 | "

each one of the two reference objects.

", None, QtGui.QApplication.UnicodeUTF8)) 284 | self.Reconfigure.setTitle(QtGui.QApplication.translate("LaserControl", "Redefine reference object:", None, QtGui.QApplication.UnicodeUTF8)) 285 | self.redef_1.setText(QtGui.QApplication.translate("LaserControl", "Reference object 1", None, QtGui.QApplication.UnicodeUTF8)) 286 | self.redef_2.setText(QtGui.QApplication.translate("LaserControl", "Reference object 2", None, QtGui.QApplication.UnicodeUTF8)) 287 | self.redef_3.setText(QtGui.QApplication.translate("LaserControl", "Reference object 3", None, QtGui.QApplication.UnicodeUTF8)) 288 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_config), QtGui.QApplication.translate("LaserControl", "Confituration", None, QtGui.QApplication.UnicodeUTF8)) 289 | self.trackMode.setText(QtGui.QApplication.translate("LaserControl", "Tracking mode", None, QtGui.QApplication.UnicodeUTF8)) 290 | self.textEdit_2.setHtml(QtGui.QApplication.translate("LaserControl", "\n" 291 | "\n" 294 | "

Now you can start to use the device.

\n" 295 | "


\n" 296 | "

The laser will points to the equatorial

\n" 297 | "

coordinates received from Stellarium.

", None, QtGui.QApplication.UnicodeUTF8)) 298 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_move), QtGui.QApplication.translate("LaserControl", "Pointing", None, QtGui.QApplication.UnicodeUTF8)) 299 | self.menu_Archivo.setTitle(QtGui.QApplication.translate("LaserControl", "&File", None, QtGui.QApplication.UnicodeUTF8)) 300 | self.menu_Dispositivo.setTitle(QtGui.QApplication.translate("LaserControl", "&Device", None, QtGui.QApplication.UnicodeUTF8)) 301 | self.menu_Connect.setTitle(QtGui.QApplication.translate("LaserControl", "&Connect", None, QtGui.QApplication.UnicodeUTF8)) 302 | self.menu_Ayuda.setTitle(QtGui.QApplication.translate("LaserControl", "&Help", None, QtGui.QApplication.UnicodeUTF8)) 303 | self.action_Cerrar.setText(QtGui.QApplication.translate("LaserControl", "&Quit", None, QtGui.QApplication.UnicodeUTF8)) 304 | self.action_Iniciar.setText(QtGui.QApplication.translate("LaserControl", "&Iniciar", None, QtGui.QApplication.UnicodeUTF8)) 305 | self.action_Detener.setText(QtGui.QApplication.translate("LaserControl", "&Detener", None, QtGui.QApplication.UnicodeUTF8)) 306 | self.action_Desconectar.setText(QtGui.QApplication.translate("LaserControl", "&Desconectar", None, QtGui.QApplication.UnicodeUTF8)) 307 | self.actionAcerca_de.setText(QtGui.QApplication.translate("LaserControl", "About ..", None, QtGui.QApplication.UnicodeUTF8)) 308 | self.actionCargar.setText(QtGui.QApplication.translate("LaserControl", "Cargar", None, QtGui.QApplication.UnicodeUTF8)) 309 | self.actionGuardar_configuraci_n.setText(QtGui.QApplication.translate("LaserControl", "Guardar en archivo", None, QtGui.QApplication.UnicodeUTF8)) 310 | self.actionCargar_configuraci_n.setText(QtGui.QApplication.translate("LaserControl", "Cargar desde archivo", None, QtGui.QApplication.UnicodeUTF8)) 311 | self.action_Recalibrar.setText(QtGui.QApplication.translate("LaserControl", "Recalibrar", None, QtGui.QApplication.UnicodeUTF8)) 312 | self.action_Refresh.setText(QtGui.QApplication.translate("LaserControl", "Refresh Port List", None, QtGui.QApplication.UnicodeUTF8)) 313 | 314 | import icons_set_rc 315 | -------------------------------------------------------------------------------- /testing/device/README.md: -------------------------------------------------------------------------------- 1 | Testing Arduino software 2 | --------------------------- 3 | 4 | Communications with the device and Coordinate transformations. 5 | 6 | Sends commands and equatorial coordinates to the device, and receives the transformation results. 7 | 8 | *Note that two reference objects are required for initial configuration in order to obtain the transformation 9 | matrix.* 10 | 11 | 12 | ### Example of use 13 | 14 | Data: 15 | 16 | Date and time: 22:02 - August 9, 2012 17 | Lat: N 37° 24' 0.01" 18 | Long: W 5° 58' 48.00" 19 | 20 | __Obj__ __RA/DEC (J2000)__ __Time__ __Az/Alt__ 21 | 22 | Polaris 2h31m49s/81º15'51'' 22h04m20s 0º27'09''/36º49'17'' 23 | Vega 18h36m56s/38º47'03'' 22h05m07s 78º10'04''/70º05'19'' 24 | 25 | Altair 19h50m47s/8º52'07'' 22h06m11s 114º36'45''/41º30'22'' 26 | Lagoon Nebula 18h03m48s/-24º23'00'' 22h06m52s 163º02'47''/26º15'16'' 27 | Mars 13h17m55s/-8º29'04'' 22h07m51s 240º18'46''/21º04'41'' 28 | 29 | 30 | Example with given data and the ipython console output _(Note the RECV lines with the results)_: 31 | 32 | In [1]: from testing import * 33 | 34 | In [2]: t = Testing() 35 | 36 | In [3]: t.init() 37 | 38 | In [4]: t.setTime("22h02m0s") 39 | 40 | In [5]: t.setRef(1 , "2h31m49s", "89º15'51''", "22h04m20s", "0º27'09''", "36º49'17''") 41 | SEND: +0.662425/+1.557954 - +5.778494 - +0.007898/+0.642654 42 | 43 | In [6]: t.setRef(2 , "18h36m56s", "38º47'03''", "22h05m07s", "78º10'04''", "70º05'19''") 44 | SEND: +4.873541/+0.676911 - +5.781912 - +1.364285/+1.223277 45 | 46 | In [7]: t.goto("19h50m47s", "8º52'07''", "22h06m11s") 47 | SEND: +5.195772/+0.154786 - +5.786566 48 | RECV: 2.000384/0.724421 (114º36'49'' / 41º30'23'') 49 | 50 | In [8]: t.goto("18h03m48s", "-24º23'00''", "22h06m52s") 51 | SEND: +4.728970/-0.425569 - +5.789548 52 | RECV: 2.845698/0.458214 (163º2'47'' / 26º15'13'') 53 | 54 | In [9]: t.goto("13h17m55s", "-8º29'04''", "22h07m51s") 55 | SEND: +3.481568/-0.148081 - +5.793839 56 | RECV: -2.088903/0.367851 (240º18'53'' / 21º4'35'') 57 | 58 | 59 | ([More info...](http://yoestuveaqui.es/blog/communications-between-python-and-arduino-usb-serial/)) 60 | -------------------------------------------------------------------------------- /testing/device/arduino/example/CoordsLib.cpp: -------------------------------------------------------------------------------- 1 | // #include "WProgram.h" // Arduino < 1.0 2 | #include //Arduino >= 1.0 3 | #include "CoordsLib.h" 4 | 5 | CoordsLib::CoordsLib(){ 6 | _t0 = 0; 7 | _k = 1.002737908; // Constant.. Relationship between the solar time (M) and the sidereal time (S): (S = M * 1.002737908) 8 | _isSetR1 = false; 9 | _isSetR2 = false; 10 | _isSetR3 = false; 11 | } 12 | 13 | /* 14 | * Calculates the inverse of the m[3x3] matrix and returns it in the second parameter. 15 | */ 16 | void CoordsLib::_inv(float m[3][3], float res[3][3]){ 17 | float idet; 18 | 19 | //Inverse of the determinant 20 | idet = 1/( 21 | (m[0][0]*m[1][1]*m[2][2]) + (m[0][1]*m[1][2]*m[2][0]) + (m[0][2]*m[1][0]*m[2][1]) 22 | - (m[0][2]*m[1][1]*m[2][0]) - (m[0][1]*m[1][0]*m[2][2]) - (m[0][0]*m[1][2]*m[2][1]) 23 | ); 24 | 25 | res[0][0] = ((m[1][1]*m[2][2]) - (m[2][1]*m[1][2]))*idet; 26 | res[0][1] = ((m[2][1]*m[0][2]) - (m[0][1]*m[2][2]))*idet; 27 | res[0][2] = ((m[0][1]*m[1][2]) - (m[1][1]*m[0][2]))*idet; 28 | 29 | res[1][0] = ((m[1][2]*m[2][0]) - (m[2][2]*m[1][0]))*idet; 30 | res[1][1] = ((m[2][2]*m[0][0]) - (m[0][2]*m[2][0]))*idet; 31 | res[1][2] = ((m[0][2]*m[1][0]) - (m[1][2]*m[0][0]))*idet; 32 | 33 | res[2][0] = ((m[1][0]*m[2][1]) - (m[2][0]*m[1][1]))*idet; 34 | res[2][1] = ((m[2][0]*m[0][1]) - (m[0][0]*m[2][1]))*idet; 35 | res[2][2] = ((m[0][0]*m[1][1]) - (m[1][0]*m[0][1]))*idet; 36 | } 37 | 38 | /* 39 | * Multiplies two matrices, m1[3x3] and m2[3x3], and returns the result in 40 | * the third parameter. 41 | */ 42 | void CoordsLib::_m_prod(float m1[3][3], float m2[3][3], float res[3][3]){ 43 | for(int i=0; i<3; i++) 44 | for(int j=0; j<3; j++){ 45 | res[i][j] = 0.0; 46 | for(int k=0; k<3; k++) //multiplying row by column 47 | res[i][j] += m1[i][k] * m2[k][j]; 48 | } 49 | } 50 | 51 | /* 52 | * Calculates the Vector cosines (EVC) from the equatorial coordinates (ar, dec, t). 53 | */ 54 | void CoordsLib::_setEVC(float ar, float dec, float t, float* EVC){ 55 | EVC[0] = cos(dec)*cos(ar - _k*(t-_t0)); 56 | EVC[1] = cos(dec)*sin(ar - _k*(t-_t0)); 57 | EVC[2] = sin(dec); 58 | } 59 | 60 | /* 61 | * Calculates the Vector cosines (HVC) from the horizontal coordinates (ac, alt). 62 | */ 63 | void CoordsLib::_setHVC(float ac, float alt, float* HVC){ 64 | HVC[0] = cos(alt)*cos(ac); 65 | HVC[1] = cos(alt)*sin(ac); 66 | HVC[2] = sin(alt); 67 | } 68 | 69 | /* 70 | * Sets the initial observation time. 71 | */ 72 | void CoordsLib::setTime(float t0){ 73 | _t0 = t0; 74 | } 75 | 76 | /* 77 | * Sets the first reference object. 78 | * If all the reference objects have been established, calls the function that calculates T and iT. 79 | */ 80 | void CoordsLib::setRef_1(float ar, float dec, float t, float ac, float alt){ 81 | _setEVC(ar, dec, t, _LMN1); 82 | _setHVC(ac, alt, _lmn1); 83 | _isSetR1 = true; 84 | _isSetR3 = false; 85 | 86 | if(_isSetR1 && _isSetR2 && _isSetR3) 87 | _setT(); 88 | } 89 | 90 | /* 91 | * Sets the second reference object. 92 | * If all the reference objects have been established, calls the function that calculates T and iT. 93 | */ 94 | void CoordsLib::setRef_2(float ar, float dec, float t, float ac, float alt){ 95 | _setEVC(ar, dec, t, _LMN2); 96 | _setHVC(ac, alt, _lmn2); 97 | _isSetR2 = true; 98 | _isSetR3 = false; 99 | 100 | if(_isSetR1 && _isSetR2 && _isSetR3) 101 | _setT(); 102 | } 103 | 104 | /* 105 | * Sets the third reference object. 106 | * If all the reference objects have been established, calls the function that calculates T and iT. 107 | */ 108 | void CoordsLib::setRef_3(float ar, float dec, float t, float ac, float alt){ 109 | _setEVC(ar, dec, t, _LMN3); 110 | _setHVC(ac, alt, _lmn3); 111 | _isSetR3 = true; 112 | 113 | if(_isSetR1 && _isSetR2 && _isSetR3) 114 | _setT(); 115 | } 116 | 117 | /** 118 | * Indicates if the three reference objects have been established. 119 | */ 120 | bool CoordsLib::isConfigured(){ 121 | return (_isSetR1 && _isSetR2 && _isSetR3); 122 | } 123 | 124 | /* 125 | * Third reference object calculated from the cross product of the two first ones. 126 | * Then calls the function that calculates T and iT. 127 | */ 128 | void CoordsLib::autoRef_3(){ 129 | float sqrt1, sqrt2; 130 | 131 | if(_isSetR1 && _isSetR2){ 132 | sqrt1 = (1/( sqrt( pow(( (_lmn1[1]*_lmn2[2]) - (_lmn1[2]*_lmn2[1])),2) + 133 | pow(( (_lmn1[2]*_lmn2[0]) - (_lmn1[0]*_lmn2[2])),2) + 134 | pow(( (_lmn1[0]*_lmn2[1]) - (_lmn1[1]*_lmn2[0])),2)) 135 | )); 136 | _lmn3[0] = sqrt1 * ( (_lmn1[1]*_lmn2[2]) - (_lmn1[2]*_lmn2[1]) ); 137 | _lmn3[1] = sqrt1 * ( (_lmn1[2]*_lmn2[0]) - (_lmn1[0]*_lmn2[2]) ); 138 | _lmn3[2] = sqrt1 * ( (_lmn1[0]*_lmn2[1]) - (_lmn1[1]*_lmn2[0]) ); 139 | 140 | sqrt2 = (1/( sqrt( pow(( (_LMN1[1]*_LMN2[2]) - (_LMN1[2]*_LMN2[1])),2) + 141 | pow(( (_LMN1[2]*_LMN2[0]) - (_LMN1[0]*_LMN2[2])),2) + 142 | pow(( (_LMN1[0]*_LMN2[1]) - (_LMN1[1]*_LMN2[0])),2)) 143 | )); 144 | _LMN3[0] = sqrt2 * ( (_LMN1[1]*_LMN2[2]) - (_LMN1[2]*_LMN2[1]) ); 145 | _LMN3[1] = sqrt2 * ( (_LMN1[2]*_LMN2[0]) - (_LMN1[0]*_LMN2[2]) ); 146 | _LMN3[2] = sqrt2 * ( (_LMN1[0]*_LMN2[1]) - (_LMN1[1]*_LMN2[0]) ); 147 | _isSetR3 = true; 148 | 149 | if(_isSetR1 && _isSetR2 && _isSetR3) 150 | _setT(); 151 | } 152 | } 153 | 154 | /* 155 | * Sets the transformation matrix and its inverse (T and iT, respectively). 156 | */ 157 | void CoordsLib::_setT(){ 158 | float subT1[3][3], subT2[3][3], aux[3][3]; 159 | 160 | subT1[0][0] = _lmn1[0]; subT1[0][1] = _lmn2[0]; subT1[0][2] = _lmn3[0]; 161 | subT1[1][0] = _lmn1[1]; subT1[1][1] = _lmn2[1]; subT1[1][2] = _lmn3[1]; 162 | subT1[2][0] = _lmn1[2]; subT1[2][1] = _lmn2[2]; subT1[2][2] = _lmn3[2]; 163 | 164 | subT2[0][0] = _LMN1[0]; subT2[0][1] = _LMN2[0]; subT2[0][2] = _LMN3[0]; 165 | subT2[1][0] = _LMN1[1]; subT2[1][1] = _LMN2[1]; subT2[1][2] = _LMN3[1]; 166 | subT2[2][0] = _LMN1[2]; subT2[2][1] = _LMN2[2]; subT2[2][2] = _LMN3[2]; 167 | 168 | _inv(subT2, aux); 169 | _m_prod(subT1, aux, _T); 170 | _inv(_T, _iT); 171 | } 172 | 173 | /* 174 | * Horizontal coordinates (ac, alt) obtained from equatorial ones and time (ar, dec, t). 175 | * 176 | * If the third reference object is not established, it calculates it by calling the 177 | * proper function. 178 | */ 179 | void CoordsLib::getHCoords(float ar, float dec, float t, float *ac, float *alt){ 180 | float HVC[3]; 181 | float EVC[3]; 182 | _setEVC(ar, dec, t, EVC); 183 | 184 | if(!_isSetR3) 185 | autoRef_3(); 186 | 187 | for(int i=0; i<3; i++) 188 | HVC[i] = 0.0; 189 | for(int i=0; i<3; i++) 190 | for(int j=0; j<3; j++) 191 | HVC[i] += _T[i][j] * EVC[j]; 192 | 193 | (*ac) = atan2(HVC[1], HVC[0]); 194 | (*alt) = asin(HVC[2]); 195 | } 196 | 197 | /* 198 | * Equatorial coordinates (ar, dec) obtained from horizontal ones and time (ac, alt, t). 199 | * 200 | * If the third reference object is not established, it calculates it by calling the 201 | * proper function. 202 | */ 203 | void CoordsLib::getECoords(float ac, float alt, float t, float *ar, float *dec){ 204 | float HVC[3]; 205 | float EVC[3]; 206 | _setHVC(ac, alt, HVC); 207 | 208 | if(!_isSetR3) 209 | autoRef_3(); 210 | 211 | for(int i=0; i<3; i++) 212 | EVC[i] = 0.0; 213 | for(int i=0; i<3; i++) 214 | for(int j=0; j<3; j++) 215 | EVC[i] += _iT[i][j] * HVC[j]; 216 | 217 | (*ar) = atan2(EVC[1], EVC[0]) + (_k*(t-_t0)); 218 | (*dec) = asin(EVC[2]); 219 | } 220 | -------------------------------------------------------------------------------- /testing/device/arduino/example/CoordsLib.h: -------------------------------------------------------------------------------- 1 | #ifndef CoordsLib_h 2 | #define CoordsLib_h 3 | 4 | #include 5 | 6 | /** 7 | * \brief Library for coordinates transformations. Calculates the equivalent coordinates between both coordinate systems equatorial and horizontal. 8 | * 9 | * It's based on Toshimi Taki's matrix method for coordinates transformation: http://www.geocities.jp/toshimi_taki/matrix/matrix.htm 10 | * Contains the necessary methods for setting the initial time, the reference objects, the transformation matrix, and to 11 | * calculate the equivalent vectors between both coordinate systems. 12 | */ 13 | class CoordsLib{ 14 | private: 15 | 16 | /** 17 | * Constant of multiplication for the solar and sidereal time relation. 18 | */ 19 | float _k; 20 | 21 | /** 22 | * Initial timestamp for the observations. 23 | */ 24 | float _t0; 25 | 26 | /** 27 | * Indicators for definition of the three reference objects. 28 | */ 29 | bool _isSetR1, _isSetR2, _isSetR3; 30 | 31 | /** 32 | * Auxiliary matrices. 33 | */ 34 | float _lmn1[3], _LMN1[3], _lmn2[3], _LMN2[3], _lmn3[3], _LMN3[3]; 35 | 36 | /** 37 | * Transformation matrix. Transform vectors from equatorial to horizontal system. 38 | */ 39 | float _T[3][3]; 40 | 41 | /** 42 | * Inverse transformation matrix. Transform vectors from horizontal to equatorial system. 43 | */ 44 | float _iT[3][3]; 45 | 46 | /** 47 | * If the three reference objects have been defined, it calculates the transformation matrix from them. 48 | */ 49 | void _setT(); 50 | 51 | /** 52 | * Obtains a vector in polar notation from the equatorial coordinates and the observation time. 53 | * 54 | * \param ar Right ascension. 55 | * \param dec Declination. 56 | * \param t Timestamp of the observation. 57 | * \param *EVC Pointer to array: Returns the three dimensional vector in polar notation. 58 | */ 59 | void _setEVC(float ar, float dec, float t, float* EVC); 60 | 61 | /** 62 | * Obtains a vector in polar notation from the horizontal coordinates and observation time. 63 | * 64 | * \param ac Azimuth. 65 | * \param alt Altitude. 66 | * \param t Timestamp of the observation. 67 | * \param *HVC Pointer to array: Returns the three dimensional vector in polar notation. 68 | */ 69 | void _setHVC(float ac, float alt, float* HVC); 70 | 71 | /** 72 | * Calculates the 3x3 inverse matrix. 73 | * 74 | * \param m[3][3] Input matrix. 75 | * \param res[3][3] Pointer to array: Returns the inverse matrix. 76 | */ 77 | void _inv(float m[3][3], float res[3][3]); 78 | 79 | /** 80 | * Calculates the product of 3x3 matrices. 81 | * 82 | * \param m1[3][3] Input matrix 1. 83 | * \param m2[3][3] Input matrix 2. 84 | * \param res[3][3] Pointer to array: Returns the result matrix. 85 | */ 86 | void _m_prod(float m1[3][3], float m2[3][3], float res[3][3]); 87 | 88 | public: 89 | 90 | /** 91 | * Class constructor. 92 | */ 93 | CoordsLib(); 94 | 95 | /** 96 | * Sets the initial time. 97 | * 98 | * This parameter is used in order to consider time passing on horizontal coordinates system. 99 | * 100 | * \param t0 Unix Timestamp of the initial observation time. 101 | */ 102 | void setTime(float t0); 103 | 104 | /** 105 | * Sets the first reference object from the coordinates in both coordinates systems for 106 | * that object. 107 | * 108 | * \param ar Right Ascension (equatorial coordinates). 109 | * \param dec Declination (equatorial coordinates). 110 | * \param t Unix Timestamp of the Observation. 111 | * \param ac Azimuth (horizontal coordinates). 112 | * \param alt Altitude (horizontal coordinates). 113 | */ 114 | void setRef_1(float ar, float dec, float t, float ac, float alt); 115 | 116 | /** 117 | * Sets the second reference object from the coordinates in both coordinates systems for 118 | * that object. 119 | * 120 | * \param ar Right Ascension (equatorial coordinates). 121 | * \param dec Declination (equatorial coordinates). 122 | * \param t Unix Timestamp of the Observation. 123 | * \param ac Azimuth (horizontal coordinates). 124 | * \param alt Altitude (horizontal coordinates). 125 | */ 126 | void setRef_2(float ar, float dec, float t, float ac, float alt); 127 | 128 | /** 129 | * Sets the third reference object from the coordinates in both coordinates systems for 130 | * that object. 131 | * 132 | * \param ar Right Ascension (equatorial coordinates). 133 | * \param dec Declination (equatorial coordinates). 134 | * \param t Unix Timestamp of the Observation. 135 | * \param ac Azimuth (horizontal coordinates). 136 | * \param alt Altitude (horizontal coordinates). 137 | */ 138 | void setRef_3(float ar, float dec, float t, float ac, float alt); 139 | 140 | /** 141 | * Indicates if the three reference objects has been calculated. 142 | * 143 | * \return Boolean. 144 | */ 145 | bool isConfigured(); 146 | 147 | /** 148 | * Third reference object calculated from the two others ones. 149 | * 150 | * Calculates the cross product of the two first reference objects in both coordinates systems, in order 151 | * to obtain the third one. 152 | * These two first objects must have 90º from each other, approximately (from 60º to 120º is enough to obtain 153 | * goods results). 154 | */ 155 | void autoRef_3(); 156 | 157 | /** 158 | * Horizontal coordinates calculated from the equatorial ones and time. 159 | * 160 | * \param ar Right Ascension (equatorial coordinates). 161 | * \param dec Declination (equatorial coordinates) 162 | * \param t Unix Timestamp of the Observation. 163 | * \param *ac Pointer to float: Returns the azimuth (horizontal coordiantes). 164 | * \param *alt Pointer to float: Returns the altitude (horizontal coordinates). 165 | */ 166 | void getHCoords(float ar, float dec, float t, float *ac, float *alt); 167 | 168 | /** 169 | * Equatorial coordinates calculated from the horizontal ones and time. 170 | * 171 | * \param ac Azimuth (horizontal coordinates). 172 | * \param alt Altitude (horizontal coordinates). 173 | * \param t Unix Timestamp of the Observation. 174 | * \param *ar Pointer to float: Returns the right ascension (equatorial coordinates). 175 | * \param *dec Pointer to float: Returns the declination (equatorial coordinates). 176 | */ 177 | void getECoords(float ac, float alt, float t, float *ar, float *dec); 178 | }; 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /testing/device/arduino/example/example.pde: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "CoordsLib.h" //Library for coordinate conversion 5 | 6 | CoordsLib Coords = CoordsLib(); 7 | 8 | void setup(){ 9 | Serial.begin(9600); 10 | } 11 | 12 | /* 13 | * Get a float value from the serial port, and send the '_OK_' ack string. 14 | * The value must conains six decimals, in a string with 9 bytes, including sign and decimal dot. 15 | * Examples: '-0.036526', '+5.238388' 16 | * 17 | * \return float. 18 | */ 19 | float serialGetFloat(){ 20 | char bytes[9], sign; 21 | int nbytes = 0; 22 | float fex; 23 | bool recv = false; 24 | 25 | bytes[8] = '\0'; 26 | Serial.println("float"); 27 | while(!recv){ 28 | if (Serial.available() > 0) { 29 | sign = Serial.read(); 30 | while(nbytes < 8) 31 | if(Serial.available() > 0){ 32 | bytes[nbytes] = Serial.read(); 33 | nbytes++; 34 | } 35 | fex = strtod(bytes, NULL); 36 | if(sign=='-') 37 | fex = 0.0 - fex; 38 | recv = true; 39 | } 40 | } 41 | Serial.println("_OK_"); 42 | return fex; 43 | } 44 | 45 | /* 46 | * Main loop. 47 | * 48 | * 49 | */ 50 | void loop(){ 51 | float t0; 52 | float ar, dec, t; 53 | float ac, alt; 54 | char comm[5]; 55 | int bytes_recv = 0; 56 | 57 | comm[4]='\0'; 58 | Serial.println("cmd"); 59 | while(bytes_recv < 4){ 60 | //Waiting for a new command... 61 | if (Serial.available() > 0) 62 | comm[bytes_recv++] = Serial.read(); 63 | } 64 | 65 | //Getting the command params (float values). The number of params depends on the command: 66 | if(strcmp(comm, "set1")==0 || strcmp(comm, "set2")==0 || strcmp(comm, "goto")==0){ 67 | ar = serialGetFloat(); 68 | dec = serialGetFloat(); 69 | t = serialGetFloat(); 70 | } 71 | if(strcmp(comm, "set1")==0 || strcmp(comm, "set2")==0){ 72 | ac = (2 * M_PI - serialGetFloat()); //The Azimuth is measured in opposite direction of the Right Ascension 73 | alt = serialGetFloat(); 74 | } 75 | 76 | //Processing command, and returning results and ack by the serial port: 77 | if(strcmp(comm, "time")==0){ 78 | t0 = serialGetFloat(); 79 | Coords.setTime(t0); 80 | Serial.println(); 81 | Serial.println("done_time"); 82 | }else if(strcmp(comm, "set1")==0){ 83 | Coords.setRef_1(ar, dec, t, ac, alt); 84 | Serial.println(); 85 | Serial.println("done_set1"); 86 | }else if(strcmp(comm, "set2")==0){ 87 | Coords.setRef_2(ar, dec, t, ac, alt); 88 | Serial.println(); 89 | Serial.println("done_set2"); 90 | }else if(strcmp(comm, "goto")==0){ 91 | Coords.getHCoords(ar, dec, t, &ac, &alt); 92 | Serial.println();Serial.print((0.0 - ac), 6); Serial.print(' '); Serial.print(alt, 6);Serial.println(); 93 | Serial.println("done_goto"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /testing/device/testing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | import math 6 | import serial 7 | 8 | ## \brief Class for testing purposes.. 9 | # 10 | # Implements the set of available commands to send to the device (Arduino). 11 | # Also prints both sent data and results received from Arduino. 12 | # Includes some useful functions to units transformation (degrees, hours, radians..). 13 | # 14 | # An example of use would be as follows: 15 | # 16 | # from testing import * 17 | # t = Testing() 18 | # t.init() 19 | # t.setTime("22h02m0s") 20 | # t.setRef(1 , "2h31m49s", "89º15'51''", "22h04m20s", "0º27'09''", "36º49'17''") 21 | # t.setRef(2 , "18h36m56s", "38º47'03''", "22h05m07s", "78º10'04''", "70º05'19''") 22 | # 23 | # t.goto("19h50m47s", "8º52'07''", "22h06m11s") 24 | # t.goto("18h03m48s", "-24º23'00''", "22h06m52s") 25 | # t.goto("13h17m55s", "-8º29'04''", "22h07m51s") 26 | # 27 | # 28 | class Testing(): 29 | def __init__(self, usb_serial='/dev/ttyUSB0', usb_serial_baud=9600, timeout=2): 30 | self.serial = serial.Serial(usb_serial, usb_serial_baud, timeout=timeout) 31 | 32 | def sread(self): 33 | line = self.serial.readline().rstrip() 34 | exp = re.compile('^((done.*$)|(cmd))$') 35 | return line 36 | 37 | def setTime(self, time): 38 | self.serial.write("time") 39 | resp = self.sread() 40 | if(resp == 'float'): 41 | self.serial.write(self.hours2rad(time)) 42 | while(resp != 'cmd'): 43 | resp = self.sread() 44 | 45 | def init(self): 46 | exp = re.compile('^(-?)[0-9]{1,9} (-?)[0-9]{1,9}$') 47 | self.serial.write('init') 48 | resp = self.sread() 49 | while(resp != 'cmd'): 50 | if(exp.match(resp)): 51 | resp_d = resp.split(' ') 52 | print("(%s / %s)" % (resp_d[0], resp_d[1])) 53 | resp = self.sread() 54 | 55 | def movx(self, signDir): 56 | self.serial.write('movx') 57 | self.serial.write(signDir) 58 | def movy(self, signDir): 59 | self.serial.write('movy') 60 | self.serial.write(signDir) 61 | def stop(self): 62 | exp = re.compile('^h_.*$') 63 | self.serial.write('stop') 64 | resp = self.sread() 65 | while(resp != 'done'): 66 | if(exp.match(resp)): 67 | resp = resp.replace("h_", '') 68 | resp_d = resp.split(' ') 69 | print("(%s / %s)" % (resp_d[0], resp_d[1])) 70 | resp = self.sread() 71 | 72 | def setRef(self, id_ref, ra, dec, time, ac, alt): 73 | setf = {1: 'set1', 2: 'set2', 3: 'set3'} 74 | 75 | self.serial.write(setf[id_ref]) 76 | resp = self.sread() 77 | if(resp == 'float'): 78 | self.serial.write(self.hours2rad(ra)) 79 | self.serial.write(self.deg2rad(dec)) 80 | self.serial.write(self.hours2rad(time)) 81 | self.serial.write(self.deg2rad(ac)) 82 | self.serial.write(self.deg2rad(alt)) 83 | print("SEND: %s/%s - %s - %s/%s" % (self.hours2rad(ra), self.deg2rad(dec), self.hours2rad(time), self.deg2rad(ac), self.deg2rad(alt))) 84 | while(resp != 'cmd'): 85 | resp = self.sread() 86 | 87 | def goto(self, ra, dec, time): 88 | exp = re.compile('^(-?)[0-9]{1}.[0-9]{4,8} (-?)[0-9]{1}.[0-9]{4,8}$') 89 | self.serial.write('goto') 90 | resp = self.sread() 91 | if(resp == 'float'): 92 | self.serial.write(self.hours2rad(ra)) 93 | self.serial.write(self.deg2rad(dec)) 94 | self.serial.write(self.hours2rad(time)) 95 | print("SEND: %s/%s - %s" % (self.hours2rad(ra), self.deg2rad(dec), self.hours2rad(time))) 96 | while(resp != 'cmd'): 97 | if(exp.match(resp)): 98 | resp_d = resp.split(' ') 99 | print("RECV: %s/%s (%s / %s)" % (resp_d[0], resp_d[1], self.rad2deg(resp_d[0]), self.rad2deg(resp_d[1]))) 100 | resp = self.sread() 101 | 102 | # h = HhMmSs => H+(M/60)+(S/60^2) hours 103 | # From hours to radians: (nhours * 15 * pi)/180 104 | def hours2rad(self, h): 105 | exp = re.compile('^[0-9]{,3}h[0-9]{,3}m[0-9]{,3}s$') 106 | if(not exp.match(h)): 107 | return None 108 | 109 | h = h.replace('h','.').replace("m",'.').replace("s",'.') 110 | h_dic = h.split('.') 111 | 112 | h_h = float(h_dic[0]) 113 | h_m = float(h_dic[1]) 114 | h_s = float(h_dic[2]) 115 | 116 | nh = (h_h+(h_m/60)+(h_s/(60**2))) 117 | 118 | res = round((nh * 15 * math.pi) / 180, 6) 119 | if(res < 0.0): return '%f' % res; 120 | else: return '+%f' % res; 121 | 122 | # d = DºM'S'' => D+(M/60)+(S/60^2) degrees || D.dº 123 | # From degrees to radians: (ndeg * pi)/180 124 | def deg2rad(self, d): 125 | exp1 = re.compile('^-?[0-9]{,3}(º|ᵒ)[0-9]{,3}'[0-9]{,3}([']{2}|")$') 126 | exp2 = re.compile('^-?[0-9]{,3}.[0-9]{,6}(º|ᵒ)$') 127 | 128 | if(not exp1.match(d) and not exp2.match(d)): 129 | return None 130 | elif(exp1.match(d)): 131 | d = d.replace('º','.').replace("''",'.').replace("'",'.') 132 | d_dic = d.split('.') 133 | d_deg = float(d_dic[0]) 134 | d_min = float(d_dic[1]) 135 | d_sec = float(d_dic[2]) 136 | 137 | if(d_deg < 0): 138 | d_min = 0 - d_min; 139 | d_sec = 0 - d_sec; 140 | 141 | d_ndeg = (d_deg+(d_min/60)+(d_sec/(60**2))) 142 | else: 143 | d_ndeg = float(d.replace('º','')) 144 | if(d_ndeg < 0): d_ndeg = 360 - abs(d_ndeg); 145 | 146 | res = round((d_ndeg * math.pi) / 180, 6) 147 | if(res < 0): return '%f' % res; 148 | else: return '+%f' % res; 149 | 150 | # DºM'S'' 151 | # From radians to degrees: (rad * 180)/pi 152 | def rad2deg(self, rad): 153 | exp = re.compile('^(-?)[0-9]{1}.[0-9]{4,8}') 154 | 155 | if(not exp.match(rad)): 156 | return None 157 | 158 | r = float(rad) 159 | if(r < 0): 160 | r = (2 * math.pi) - abs(r) 161 | 162 | ndeg = (r * 180) / math.pi 163 | deg = math.floor(float(ndeg)) 164 | nmins = (ndeg - deg) * 60 165 | mins = math.floor(nmins) 166 | secs = round( (nmins - mins) * 60 ) 167 | 168 | return "%dº%d'%d''" % (deg, mins, secs) 169 | -------------------------------------------------------------------------------- /testing/stellarium/README.md: -------------------------------------------------------------------------------- 1 | Testing communications with Stellarium 2 | --------------------------------------- 3 | 4 | Getting and showing the equatorial coordinates sent from Stellarium. 5 | 6 | ### Stellarium configuration 7 | 8 | > Configuration window -> Plugins -> Telescope Control -> Configure -> Add 9 | 10 | In that window you can introduce the configuration data: 11 | 12 | * Telescope controlled by: **External Software or a remote computer** 13 | * Name: **Laser pointer** (or whatever you want..) 14 | 15 | * Connection settings: 16 | * IP: **localhost** 17 | * Port: **10001** 18 | 19 | * User interface settings: 20 | * **Use field of view indicators** 21 | 22 | 23 | ### Example of use: 24 | 25 | Once the "virtual telescope" has been configured in Stellarium, you can send the selected object coordinates by **"Ctrl + 1"** keys. 26 | 27 | So now we can start the server in order to receive the coordinates, in console: 28 | 29 | ./telescope_server.py 30 | telescope_server.py: run - INFO: Telescope_Server 31 | telescope_server.py: handle_accept - DEBUG: Connected: ('127.0.0.1', 37023) 32 | telescope_server.py: handle_read - DEBUG: Size: 20, Type: 0, Time: 1342975913635132, RA: 1932791525 (e50e3473), DEC: 17943536 (f0cb1101) 33 | coords.py: *rad_2_stellarium_protocol - DEBUG: (hours, degrees): (10.800277, 1.503900) -> (10h48m1.0s, 1º30'14'')* 34 | 35 | 36 | ([More info...](http://yoestuveaqui.es/blog/communications-between-python-and-stellarium-stellarium-telescope-protocol/)) 37 | -------------------------------------------------------------------------------- /testing/stellarium/coords.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import math 5 | import re 6 | import logging 7 | from time import time, ctime, strftime, localtime 8 | 9 | # \brief Functions library for format conversions. 10 | # 11 | # Contains the necessary functions to calculate most commons format conversions used by the communications 12 | # with the device and Stellarium. 13 | 14 | 15 | ## From radians to hours, with until six decimals of precision (float) 16 | # (rads * 180)/(15 * pi) 17 | # 18 | # \param rads Radians in float format 19 | # \return Float that represents the number of hours equivalent to the received radians 20 | def rad_2_hour(rads): 21 | h = round( (rads * 180)/(15 * math.pi), 6) 22 | if h > 24.0: 23 | h = h - 24.0 24 | if h < 0.0: 25 | h = 24.0 + h 26 | return h 27 | 28 | ## Transforms from radians in a string format to degrees (float) 29 | # (rad * 180)/pi 30 | # 31 | # \param rad Signed radians in string format 32 | # \return Degrees in float format 33 | def radStr_2_deg(rad): 34 | exp = re.compile('^(-?)[0-9]{1}\.[0-9]{4,8}') 35 | 36 | if(not exp.match(rad)): 37 | return None 38 | 39 | r = float(rad) 40 | if(r < 0): 41 | r = (2 * math.pi) - abs(r) 42 | 43 | return (r * 180) / math.pi 44 | 45 | ## Transforms radians from float to string format 46 | # 47 | # \param rad Radians in float format 48 | # \return Signed radians in string format 49 | def rad_2_radStr(rad): 50 | if(rad < 0.0): return '%f' % rad; 51 | else: return '+%f' % rad; 52 | 53 | ## Transforms from radians to degrees, both in string format 54 | # 55 | # \param r Signed radians in string format 56 | # \return Degrees in string format (ej: "DºM'S''") 57 | def radStr_2_degStr(r): 58 | return deg_2_degStr(radStr_2_deg(r)) 59 | 60 | ## Tranforms from degrees in string format to radians 61 | # d = DºM'S'' => D+(M/60)+(S/60^2) degrees => D.dº 62 | # 63 | # \param d Degrees in string format ("DºM'S''" || "D.dº") 64 | # \return Radians in float format 65 | def degStr_2_rad(d): 66 | exp1 = re.compile('^-?[0-9]{,3}(º|ᵒ)[0-9]{,3}\'[0-9]{,3}([\']{2}|")$') 67 | exp2 = re.compile('^-?[0-9]{,3}\.[0-9]{,6}(º|ᵒ)$') 68 | 69 | if(not exp1.match(d) and not exp2.match(d)): 70 | logging.debug("Error parametro: %s" % d) 71 | return None 72 | elif(exp1.match(d)): 73 | d = d.replace('º','.').replace("''",'.').replace("'",'.') 74 | d_dic = d.split('.') 75 | d_deg = float(d_dic[0]) 76 | d_min = float(d_dic[1]) 77 | d_sec = float(d_dic[2]) 78 | 79 | if(d_deg < 0): 80 | d_min = 0 - d_min; 81 | d_sec = 0 - d_sec; 82 | 83 | d_ndeg = (d_deg+(d_min/60)+(d_sec/(60**2))) 84 | else: 85 | d_ndeg = float(d.replace('º','')) 86 | if(d_ndeg < 0): d_ndeg = 360 - abs(d_ndeg); 87 | 88 | return round((d_ndeg * math.pi) / 180, 6) 89 | 90 | ## Transforms from degrees to radians, both in string format 91 | # 92 | # \param d Degrees in string format ("DºM'S''" || "D.dº") 93 | # \return Signed radians in string format 94 | def degStr_2_radStr(d): 95 | return rad_2_radStr(degStr_2_rad(d)) 96 | 97 | ## Transforms degrees from float to string format. 98 | # 99 | # \param deg Degrees in float format 100 | # \return Degrees in string format ("DºM'S''") 101 | def deg_2_degStr(deg): 102 | ndeg = math.floor(float(deg)) 103 | 104 | nmins = (deg - ndeg) * 60 105 | mins = math.floor(nmins) 106 | secs = round( (nmins - mins) * 60 ) 107 | 108 | return "%dº%d'%d''" % (ndeg, mins, secs) 109 | 110 | ## From hours in string format to radians 111 | # h = HhMmSs => H+(M/60)+(S/60^2) hours 112 | # (hours * 15 * pi)/180 113 | # 114 | # \param h Hours in string format ("HhMmSSs") 115 | # \return Radians in float format 116 | def hourStr_2_rad(h): 117 | exp = re.compile('^[0-9]{,3}h[0-9]{,3}m[0-9]{,3}s$') 118 | if(not exp.match(h)): 119 | logging.debug("Error in param: %s" % h) 120 | return None 121 | 122 | h = h.replace('h','.').replace("m",'.').replace("s",'.') 123 | h_dic = h.split('.') 124 | 125 | h_h = float(h_dic[0]) 126 | h_m = float(h_dic[1]) 127 | h_s = float(h_dic[2]) 128 | 129 | nh = (h_h+(h_m/60)+(h_s/(60**2))) 130 | 131 | return round((nh * 15 * math.pi) / 180, 6) 132 | 133 | ## Transforms hours from float to string format 134 | # 135 | # \param hours Hours in float format 136 | # \return Hours in string format ("HhMmSSs") 137 | def hour_2_hourStr(hours): 138 | (h, m, s) = hour_min_sec(hours) 139 | return '%dh%dm%00.1fs' % (h, m, s) 140 | 141 | ## From hours in float format, to a list with number of hours, minutes and seconds 142 | # 143 | # \param hours Hours in float format 144 | # \return List with (hours, minutes, seconds) 145 | def hour_min_sec(hours): 146 | h = math.floor(hours) 147 | 148 | hours_m = (hours - h)*60.0 149 | m = math.floor(hours_m) 150 | 151 | s = (hours_m - m)*60.0 152 | 153 | #Avoiding the X.60 values 154 | if s >= 59.99: 155 | s = 0 156 | m += 1 157 | if m >= 60: 158 | m = 60-m 159 | h += 1 160 | 161 | return (h, m, s) 162 | 163 | ## From degrees in float format, to a list with number of degrees, minutes and seconds 164 | # 165 | # \param degs Degrees in float format 166 | # \return List with (degrees, minutes, seconds) 167 | def grad_min_sec(degs): 168 | #Avoiding operations with negative values 169 | to_neg = False 170 | if degs < 0: 171 | degs = math.fabs(degs) 172 | to_neg = True 173 | 174 | d = math.floor(degs) 175 | 176 | degs_m = (degs - d)*60.0 177 | m = math.floor(degs_m) 178 | 179 | s = (degs_m - m)*60.0 180 | 181 | #Avoiding the .60 values 182 | if s >= 59.99: 183 | s = 0 184 | m += 1 185 | if m >= 60.0: 186 | m = 60.0-m 187 | d += 1 188 | 189 | if to_neg: 190 | d = -d; 191 | 192 | return (d, m, s) 193 | 194 | ## Transforms the values obtained from "Stellarium Telescope Protocol", to a list with each value in string format 195 | # ("HhMmSSs", "DºM'S''", "HhMmSs") 196 | # 197 | # \param ra Right ascension 198 | # \param dec Declination 199 | # \param mtime Timestamp in microseconds 200 | # \return List with (Right ascension, declination, time) => ("HhMmSSs", "DºM'S''", "HhMmSs") 201 | def eCoords2str(ra, dec, mtime): 202 | ra_h = ra*12.0/2147483648 203 | dec_d = dec*90.0/1073741824 204 | time_s = math.floor(mtime / 1000000) 205 | 206 | return ('%dh%dm%00.0fs' % hour_min_sec(ra_h), '%dº%d\'%00.0f\'\'' % grad_min_sec(dec_d), strftime("%Hh%Mm%Ss", localtime(time_s))) 207 | 208 | ## Transforms coordinates from radians to J2000 string format ("HhMmSSs/GºM'SS'' at Fecha") 209 | # 210 | # \param ra Right ascension (float) 211 | # \param dec Declination (float) 212 | # \param mtime Timestamp in microseconds (float) 213 | # \return Equiatorial coordinates in J2000 string format 214 | def toJ2000(ra, dec, mtime): 215 | # HhMmSs => H+(M/60)+(S/60^2) hours 216 | # DºM'S'' => D+(M/60)+(S/60^2) degrees 217 | # From hours to radians: (hours * 15 * pi)/180 218 | 219 | ra_h = ra*12.0/2147483648 220 | (h1, m1, s1) = hour_min_sec(ra_h) 221 | 222 | dec_d = dec*90.0/1073741824 223 | (h2, m2, s2) = grad_min_sec(dec_d) 224 | 225 | time_s = math.floor(mtime / 1000000) # From microseconds to seconds (Unix timestamp) 226 | t = ctime(time_s) 227 | 228 | return '%dh%dm%00.0fs/%dº%d\'%00.1f\'\' at %s' % (h1, m1, s1, h2, m2, s2, t) 229 | 230 | 231 | ## Transforms coordinates from radians to the "Stellarium Telescope Protocol" format 232 | # 233 | # \param ra Right ascension (float) 234 | # \param dec Declination (float) 235 | # \return List with (Right ascension, Declination) in the "Stellarium Telescope Protocol" format 236 | def rad_2_stellarium_protocol(ra, dec): 237 | 238 | ra_h = rad_2_hour(ra) 239 | 240 | dec_d = (dec * 180) / math.pi 241 | 242 | logging.debug("(hours, degrees): (%f, %f)" % (ra_h, dec_d)) 243 | 244 | return (int(ra_h*(2147483648/12.0)), int(dec_d*(1073741824/90.0))) 245 | 246 | -------------------------------------------------------------------------------- /testing/stellarium/telescope_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | from PyQt4 import QtCore 6 | from threading import Thread 7 | import asyncore, socket 8 | from time import time, localtime 9 | from bitstring import BitArray, BitStream, ConstBitStream # http://code.google.com/p/python-bitstring/ 10 | import coords 11 | 12 | 13 | logging.basicConfig(level=logging.DEBUG, format="%(filename)s: %(funcName)s - %(levelname)s: %(message)s") 14 | 15 | 16 | ## \brief Implementation of the server side connection for 'Stellarium Telescope Protocol' 17 | # 18 | # Manages the execution thread to the server side connection with Stellarium 19 | class Telescope_Channel(QtCore.QThread, asyncore.dispatcher): 20 | ## @var stell_pos_recv 21 | # It emits when equatorial coordinates are received from client (Stellarium) 22 | stell_pos_recv = QtCore.pyqtSignal(str, str, str) #Ra, Dec, Time 23 | 24 | ## Class constructor 25 | # 26 | # \param conn_sock Connection socket 27 | def __init__(self, conn_sock): 28 | self.is_writable = False 29 | self.buffer = '' 30 | asyncore.dispatcher.__init__(self, conn_sock) 31 | QtCore.QThread.__init__(self, None) 32 | 33 | ## Indicates the socket is readable 34 | # 35 | # \return Boolean True/False 36 | def readable(self): 37 | return True 38 | 39 | ## Indicates the socket is writable 40 | # 41 | # \return Boolean True/False 42 | def writable(self): 43 | return self.is_writable 44 | 45 | ## Close connection handler 46 | # 47 | def handle_close(self): 48 | logging.debug("Disconnected") 49 | self.close() 50 | 51 | ## Reading socket handler 52 | # 53 | # Reads and processes client data, and throws the proper signal with coordinates as parameters 54 | def handle_read(self): 55 | #format: 20 bytes in total. Size: intle:16 56 | #Incomming messages comes with 160 bytes.. 57 | data0 = self.recv(160); 58 | if data0: 59 | data = ConstBitStream(bytes=data0, length=160) 60 | #print "All: %s" % data.bin 61 | 62 | msize = data.read('intle:16') 63 | mtype = data.read('intle:16') 64 | mtime = data.read('intle:64') 65 | 66 | # RA: 67 | ant_pos = data.bitpos 68 | ra = data.read('hex:32') 69 | data.bitpos = ant_pos 70 | ra_uint = data.read('uintle:32') 71 | 72 | # DEC: 73 | ant_pos = data.bitpos 74 | dec = data.read('hex:32') 75 | data.bitpos = ant_pos 76 | dec_int = data.read('intle:32') 77 | 78 | #______ Testing: 79 | # Sends back to Stellarium the received coordinates, in order to update the field of view indicator 80 | (sra, sdec, stime) = coords.eCoords2str(float("%f" % ra_uint), float("%f" % dec_int), float("%f" % mtime)) 81 | self.act_pos(coords.hourStr_2_rad(sra), coords.degStr_2_rad(sdec)) 82 | #______ End Testing 83 | 84 | # Emits the signal with received equatorial coordinates (for use in external Qt Gui..) 85 | self.stell_pos_recv.emit("%f" % ra_uint, "%f" % dec_int, "%f" % mtime) 86 | 87 | 88 | ## Updates the field of view indicator in Stellarium 89 | # 90 | # \param ra Right ascension in signed string format 91 | # \param dec Declination in signed string format 92 | def act_pos(self, ra, dec): 93 | (ra_p, dec_p) = coords.rad_2_stellarium_protocol(ra, dec) 94 | 95 | times = 10 #Number of times that Stellarium expects to receive new coords //Absolutly empiric.. 96 | for i in range(times): 97 | self.move(ra_p, dec_p) 98 | 99 | ## Sends to Stellarium equatorial coordinates 100 | # 101 | # Receives the coordinates in float format. Obtains the timestamp from local time 102 | # 103 | # \param ra Ascensión recta. 104 | # \param dec Declinación. 105 | def move(self, ra, dec): 106 | msize = '0x1800' 107 | mtype = '0x0000' 108 | aux_format_str = 'int:64=%r' % time() 109 | localtime = ConstBitStream(aux_format_str.replace('.', '')) 110 | 111 | sdata = ConstBitStream(msize) + ConstBitStream(mtype) 112 | sdata += ConstBitStream(intle=localtime.intle, length=64) + ConstBitStream(uintle=ra, length=32) 113 | sdata += ConstBitStream(intle=dec, length=32) + ConstBitStream(intle=0, length=32) 114 | 115 | self.buffer = sdata 116 | self.is_writable = True 117 | self.handle_write() 118 | 119 | ## Transmission handler 120 | # 121 | def handle_write(self): 122 | self.send(self.buffer.bytes) 123 | self.is_writable = False 124 | 125 | 126 | ## \brief Implementation of the server side communications for 'Stellarium Telescope Protocol'. 127 | # 128 | # Each connection request generate an independent execution thread as instance of Telescope_Channel 129 | class Telescope_Server(QtCore.QThread, asyncore.dispatcher): 130 | # @var stell_pos_recv 131 | # Proxy signal to the same name signal of the Telescope_Channel instance 132 | stell_pos_recv = QtCore.pyqtSignal(str, str, str) #Ra, Dec, Time 133 | 134 | ## Class constructor 135 | # 136 | # \param port Port to listen on 137 | # \param pos-signal Signal that will receive the coordinates to send to Stellarium 138 | def __init__(self, port=10001, pos_signal=None): 139 | asyncore.dispatcher.__init__(self, None) 140 | QtCore.QThread.__init__(self, None) 141 | self.tel = None 142 | self.port = port 143 | if pos_signal != None: 144 | pos_signal.connect(self.proxy_signal_sent) 145 | 146 | ## Starts thread 147 | # 148 | # Sets the socket to listen on 149 | def run(self): 150 | logging.info(self.__class__.__name__+" is running...") 151 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 152 | self.set_reuse_addr() 153 | self.bind(('localhost', self.port)) 154 | self.listen(1) 155 | self.connected = False 156 | asyncore.loop() 157 | 158 | ## Handles incomming connection 159 | # 160 | # Stats a new thread as Telescope_Channel instance, passing it the opened socket as parameter 161 | def handle_accept(self): 162 | self.conn, self.addr = self.accept() 163 | logging.debug('Connected: %s', self.addr) 164 | self.connected = True 165 | self.tel = Telescope_Channel(self.conn) 166 | self.tel.stell_pos_recv.connect(self.proxy_signal_recv) 167 | 168 | ## Proxy signal for receive and throw again the Telescope_Channel signal 169 | # 170 | # \param Right ascension 171 | # \param dec Declination 172 | # \param mtime Timestamp 173 | def proxy_signal_recv(self, ra, dec, mtime): 174 | self.stell_pos_recv.emit(ra, dec, mtime) 175 | 176 | ## Proxy signal for receive coordinates and send them to the Telescope_Channel threads 177 | # 178 | # \ra Right ascension 179 | # \dec Declination 180 | def proxy_signal_sent(self, ra, dec): 181 | if self.tel != None: 182 | self.tel.act_pos(ra, dec) 183 | 184 | ## Closes the connection 185 | # 186 | def close_socket(self): 187 | if self.connected: 188 | self.conn.close() 189 | 190 | #Run a Telescope Server 191 | if __name__ == '__main__': 192 | try: 193 | Server = Telescope_Server() 194 | Server.run() 195 | except KeyboardInterrupt: 196 | logging.debug("\nBye!") 197 | --------------------------------------------------------------------------------