├── screenshot.jpg ├── .gitignore ├── geonames.py ├── experimental ├── ayanamsa_start_dates.txt ├── ayanamsa_yoc.c └── ayanāmśa.md ├── vimsottari.py ├── sanskrit_names.json ├── README.md ├── gui.py ├── Gui.wxg ├── panchanga.py └── LICENSE /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skaligotla/drik-panchanga/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /geonames.py: -------------------------------------------------------------------------------- 1 | # Throw-away script for one-time use only. 2 | # 3 | # Generates a list of cities whose population is more than 50000 4 | # 5 | # Download cities15000.zip from http://download.geonames.org/export/dump/ 6 | # 7 | 8 | import csv 9 | import json 10 | 11 | fin = open('/tmp/cities15000.txt', 'r') 12 | reader = csv.reader(fin, 'excel-tab') 13 | fout = open('/tmp/cities.csv', 'w') 14 | cities = {} 15 | 16 | for record in reader: 17 | (geonameid, name, asciiname, alternatenames, latitude, longitude, 18 | featureclass, featurecode, countrycode, cc2, admin1code, 19 | admin2code, admin3code, admin4code, population, 20 | elevation, dem, timezone, modificationdate) = record 21 | 22 | # Ignore small cities 23 | if int(population) > 50000 and asciiname: 24 | cities[asciiname] = {'latitude': float(latitude), 25 | 'longitude': float(longitude), 26 | 'timezone': timezone} 27 | fout.write(u'%s:%s:%s:%s\n' % (asciiname, latitude, longitude, timezone)) 28 | 29 | fout.close() 30 | fjson = open('/tmp/cities.json', 'w') 31 | json.dump(cities, fjson) 32 | fjson.close() 33 | -------------------------------------------------------------------------------- /experimental/ayanamsa_start_dates.txt: -------------------------------------------------------------------------------- 1 | 00, name = Fagan/Bradley , julday = 1802126.27483304, year = +0221, month = 12, day = 14, hour = 018:35:45.57462260 2 | 01, name = Lahiri , julday = 1825400.08347469, year = +0285, month = 09, day = 03, hour = 014:00:12.21348569 3 | 02, name = De Luce , julday = 1721212.92673406, year = +0000, month = 06, day = 02, hour = 010:14:29.82317045 4 | 03, name = Raman , julday = 1863485.34560085, year = +0389, month = 12, day = 12, hour = 020:17:39.91341546 5 | 04, name = Ushashashi , julday = 1925433.95299605, year = +0559, month = 07, day = 23, hour = 010:52:18.85845914 6 | 05, name = Krishnamurti , julday = 1827944.66856915, year = +0292, month = 08, day = 22, hour = 004:02:44.37445208 7 | 06, name = Djwhal Khul , julday = 1706704.07496067, year = -0040, month = 09, day = 11, hour = 013:47:56.60150304 8 | 07, name = Yukteshwar , julday = 1861700.51145360, year = +0385, month = 01, day = 23, hour = 000:16:29.59113210 9 | 08, name = J.N. Bhasin , julday = 1854239.36290565, year = +0364, month = 08, day = 19, hour = 020:42:35.04805371 10 | 09, name = Babylonian/Kugler 1 , julday = 1773319.45816842, year = +0143, month = 01, day = 30, hour = 022:59:45.75136811 11 | 10, name = Babylonian/Kugler 2 , julday = 1810213.97087691, year = +0244, month = 02, day = 05, hour = 011:18:03.76542851 12 | 11, name = Babylonian/Kugler 3 , julday = 1832606.50393077, year = +0305, month = 05, day = 29, hour = 000:05:39.61821422 13 | 12, name = Babylonian/Huber , julday = 1804944.29250822, year = +0229, month = 09, day = 01, hour = 019:01:12.70979136 14 | 13, name = Babylonian/Eta Piscium , julday = 1807871.92151238, year = +0237, month = 09, day = 07, hour = 010:06:58.67001504 15 | 14, name = Babylonian/Aldebaran = 15 Tau , julday = 1801629.86565290, year = +0220, month = 08, day = 05, hour = 008:46:32.41047576 16 | 15, name = Hipparchos , julday = 1920430.15377294, year = +0545, month = 11, day = 09, hour = 015:41:25.98190919 17 | 16, name = Sassanian , julday = 1927135.87477929, year = +0564, month = 03, day = 20, hour = 008:59:40.93084171 18 | 17, name = Galact. Center = 0 Sag , julday = 1746447.51800000, year = +0069, month = 07, day = 05, hour = 000:25:55.19969165 19 | 18, name = J2000 , julday = 2451544.99999999, year = +2000, month = 01, day = 01, hour = 011:59:59.99903440 20 | 19, name = J1900 , julday = 2415019.99999998, year = +1899, month = 12, day = 31, hour = 011:59:59.99798834 21 | 20, name = B1950 , julday = 2433282.42345905, year = +1949, month = 12, day = 31, hour = 022:09:46.86153889 22 | 21, name = Suryasiddhanta , julday = 1903396.81286540, year = +0499, month = 03, day = 22, hour = 007:30:31.57027736 23 | 22, name = Suryasiddhanta, mean Sun , julday = 1909045.58263963, year = +0514, month = 09, day = 09, hour = 001:59:00.06364569 24 | 23, name = Aryabhata , julday = 1903396.78953210, year = +0499, month = 03, day = 22, hour = 006:56:55.57320580 25 | 24, name = Aryabhata, mean Sun , julday = 1909650.81334588, year = +0516, month = 05, day = 06, hour = 007:31:13.08409363 26 | 25, name = SS Revati , julday = 1924230.26070732, year = +0556, month = 04, day = 05, hour = 018:15:25.11244342 27 | 26, name = SS Citra , julday = 1847827.03048707, year = +0347, month = 01, day = 29, hour = 012:43:54.08291638 28 | 27, name = True Citra , julday = 1825430.80337965, year = +0285, month = 10, day = 04, hour = 007:16:52.00171933 29 | 28, name = True Revati , julday = 1684535.50000000, year = -0100, month = 01, day = 01, hour = 000:00:00.00002012 30 | 29, name = True Pushya , julday = 1684535.50000000, year = -0100, month = 01, day = 01, hour = 000:00:00.00002012 31 | -------------------------------------------------------------------------------- /vimsottari.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | # vimsottari.py -- routines for computing time periods of vimsottari dasha 5 | # 6 | # Copyright (C) 2015 Satish BD 7 | # Downloaded from https://github.com/bdsatish/drik-panchanga 8 | # 9 | # This file is part of the "drik-panchanga" Python library 10 | # for computing Hindu luni-solar calendar based on the Swiss ephemeris 11 | # 12 | # This program is free software: you can redistribute it and/or modify 13 | # it under the terms of the GNU Affero General Public License as published by 14 | # the Free Software Foundation, either version 3 of the License, or 15 | # (at your option) any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU Affero General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Affero General Public License 23 | # along with this program. If not, see . 24 | 25 | """ 26 | Calculates Vimshottari (=120) Dasha-bhukti-antara-sukshma-prana 27 | """ 28 | 29 | from __future__ import division 30 | from math import ceil 31 | import swisseph as swe 32 | from collections import OrderedDict as Dict 33 | from panchanga import sidereal_longitude, sidereal_year, get_planet_name 34 | 35 | swe.KETU = swe.PLUTO # I've mapped Pluto to Ketu 36 | vimsottari_year = sidereal_year # some say 360 days, others 365.25 or 365.2563 etc 37 | 38 | # Nakshatra lords, order matters. See https://en.wikipedia.org/wiki/Dasha_(astrology) 39 | adhipati_list = [ swe.KETU, swe.SUKRA, swe.SURYA, swe.CHANDRA, swe.KUJA, 40 | swe.RAHU, swe.GURU, swe.SANI, swe.BUDHA ] 41 | 42 | # (Maha-)dasha periods (in years) 43 | mahadasa = { swe.KETU: 7, swe.SUKRA: 20, swe.SURYA: 6, swe.CHANDRA: 10, swe.KUJA: 7, 44 | swe.RAHU: 18, swe.GURU: 16, swe.SANI: 19, swe.BUDHA: 17 } 45 | 46 | # assert(0 <= nak <= 26) 47 | # Return nakshatra lord (adhipati) 48 | adhipati = lambda nak: adhipati_list[nak % (len(adhipati_list))] 49 | 50 | def next_adhipati(lord): 51 | """Returns next guy after `lord` in the adhipati_list""" 52 | current = adhipati_list.index(lord) 53 | next_index = (current + 1) % len(adhipati_list) 54 | return adhipati_list[next_index] 55 | 56 | def nakshatra_position(jdut1): 57 | """Get the Nakshatra index and degrees traversed at a given JD(UT1) """ 58 | moon = sidereal_longitude(jdut1, swe.MOON) 59 | one_star = (360 / 27.) # 27 nakshatras span 360° 60 | nak = int(moon / one_star) # 0..26 61 | rem = (moon - nak * one_star) # degrees traversed in given nakshatra 62 | 63 | return [nak, rem] 64 | 65 | def dasha_start_date(jdut1): 66 | """Returns the start date (UT1) of the mahadasa which occured on or before `jd(UT1)`""" 67 | nak, rem = nakshatra_position(jdut1) 68 | one_star = (360 / 27.) # 27 nakshatras span 360° 69 | lord = adhipati(nak) # ruler of current nakshatra 70 | period = mahadasa[lord] # total years of nakshatra lord 71 | period_elapsed = rem / one_star * period # years 72 | period_elapsed *= vimsottari_year # days 73 | start_date = jdut1 - period_elapsed # so many days before current day 74 | 75 | return [lord, start_date] 76 | 77 | def vimsottari_mahadasa(jdut1): 78 | """List all mahadashas and their start dates""" 79 | lord, start_date = dasha_start_date(jdut1) 80 | retval = Dict() 81 | for i in range(9): 82 | retval[lord] = start_date 83 | start_date += mahadasa[lord] * vimsottari_year 84 | lord = next_adhipati(lord) 85 | 86 | return retval 87 | 88 | def vimsottari_bhukti(maha_lord, start_date): 89 | """Compute all bhuktis of given nakshatra-lord of Mahadasa 90 | and its start date""" 91 | lord = maha_lord 92 | retval = Dict() 93 | for i in range(9): 94 | retval[lord] = start_date 95 | factor = mahadasa[lord] * mahadasa[maha_lord] / 120. 96 | start_date += factor * vimsottari_year 97 | lord = next_adhipati(lord) 98 | 99 | return retval 100 | 101 | # North Indian tradition: dasa-antardasa-pratyantardasa 102 | # South Indian tradition: dasa-bhukti-antara-sukshma 103 | def vimsottari_antara(maha_lord, bhukti_lord, start_date): 104 | """Compute all antaradasas from given bhukit's start date. 105 | The bhukti's lord and its lord (mahadasa lord) must be given""" 106 | lord = bhukti_lord 107 | retval = Dict() 108 | for i in range(9): 109 | retval[lord] = start_date 110 | factor = mahadasa[lord] * (mahadasa[maha_lord] / 120.) 111 | factor *= (mahadasa[bhukti_lord] / 120.) 112 | start_date += factor * vimsottari_year 113 | lord = next_adhipati(lord) 114 | 115 | return retval 116 | 117 | 118 | def where_occurs(jd, some_dict): 119 | """Returns minimum key such that some_dict[key] < jd""" 120 | # It is assumed that the dict is sorted in ascending order 121 | # i.e. some_dict[i] < some_dict[j] where i < j 122 | for key in reversed(some_dict.keys()): 123 | if some_dict[key] < jd: return key 124 | 125 | 126 | def compute_antara_from(jd, mahadashas): 127 | """Returns antaradasha within which given `jd` falls""" 128 | # Find mahadasa where this JD falls 129 | i = where_occurs(jd, mahadashas) 130 | # Compute all bhuktis of that mahadasa 131 | bhuktis = vimsottari_bhukti(i, mahadashas[i]) 132 | # Find bhukti where this JD falls 133 | j = where_occurs(jd, bhuktis) 134 | # JD falls in i-th dasa / j-th bhukti 135 | # Compute all antaras of that bhukti 136 | antara = vimsottari_antara(i, j, bhuktis[j]) 137 | return (i, j, antara) 138 | 139 | # ---------------------- ALL TESTS ------------------------------ 140 | def adhipati_tests(): 141 | # nakshatra indexes counted from 0 142 | satabhisha, citta, aslesha = 23, 13, 8 143 | assert(adhipati(satabhisha) == swe.RAHU) 144 | assert(mahadasa[adhipati(satabhisha)] == 18) 145 | assert(adhipati(citta) == swe.MARS) 146 | assert(mahadasa[adhipati(citta)] == 7) 147 | assert(adhipati(aslesha) == swe.MERCURY) 148 | assert(mahadasa[adhipati(aslesha)] == 17) 149 | 150 | 151 | if __name__ == "__main__": 152 | adhipati_tests() 153 | # YYYY-MM-DD 09:40 IST = 04:10 UTC 154 | jdut1 = swe.utc_to_jd(1985, 6, 9, 4, 10, 0, flag = swe.GREG_CAL)[1] 155 | tz = 5.5 156 | print("jdut1", jdut1) 157 | dashas = vimsottari_mahadasa(jdut1) 158 | for i in dashas: 159 | print(' ---------- ' + get_planet_name(i) + ' Dasa ---------- ') 160 | bhuktis = vimsottari_bhukti(i, dashas[i]) 161 | for j in bhuktis: 162 | jd = bhuktis[j] 163 | y, m, d, h = swe.revjul(round(jd + tz)) 164 | print('%8s: %04d-%02d-%02d\t%.6lf' % (get_planet_name(j), y, m, d, jd)) 165 | 166 | jd = 2456950 # Some random date, ex: current date 167 | i, j, antara = compute_antara_from(jd, dashas) 168 | print("---- JD %d falls in %s dasa/%s bhukti -----" % 169 | (jd, get_planet_name(i), get_planet_name(j))) 170 | for k in antara: 171 | jd = antara[k] 172 | y, m, d, h = swe.revjul(round(jd + tz)) 173 | print('%8s: %04d-%02d-%02d\t%.6lf' % (get_planet_name(k), y, m, d, jd)) 174 | -------------------------------------------------------------------------------- /sanskrit_names.json: -------------------------------------------------------------------------------- 1 | { 2 | "masas": { "1":"Caitra", 3 | "2":"Vaiśākha", 4 | "3":"Jyeṣṭha", 5 | "4":"Āṣāḍha", 6 | "5":"Śrāvaṇa", 7 | "6":"Bhādrapada", 8 | "7":"Āśvina", 9 | "8":"Kārtika", 10 | "9":"Mārgaśīrṣa", 11 | "10":"Puṣya", 12 | "11":"Māgha", 13 | "12":"Phālguṇa" 14 | }, 15 | 16 | "tithis": { "1": "Śukla pakṣa prathamā", 17 | "2": "Śukla pakṣa dvitīyā", 18 | "3": "Śukla pakṣa tṛtīyā", 19 | "4": "Śukla pakṣa caturthī", 20 | "5": "Śukla pakṣa pañcamī", 21 | "6": "Śukla pakṣa ṣaṣṭhī", 22 | "7": "Śukla pakṣa saptamī", 23 | "8": "Śukla pakṣa aṣṭhamī", 24 | "9": "Śukla pakṣa navamī", 25 | "10": "Śukla pakṣa daśamī", 26 | "11": "Śukla pakṣa ekādaśī", 27 | "12": "Śukla pakṣa dvādaśī", 28 | "13": "Śukla pakṣa trayodaśī", 29 | "14": "Śukla pakṣa caturdasī", 30 | "15": "Pūrṇimā", 31 | "16": "Kṛṣṇa pakṣa prathamā", 32 | "17": "Kṛṣṇa pakṣa dvitīyā", 33 | "18": "Kṛṣṇa pakṣa tṛtīyā", 34 | "19": "Kṛṣṇa pakṣa caturthī", 35 | "20": "Kṛṣṇa pakṣa pañcamī", 36 | "21": "Kṛṣṇa pakṣa ṣaṣṭhi", 37 | "22": "Kṛṣṇa pakṣa saptamī", 38 | "23": "Kṛṣṇa pakṣa aṣṭhamī", 39 | "24": "Kṛṣṇa pakṣa navamī", 40 | "25": "Kṛṣṇa pakṣa daśamī", 41 | "26": "Kṛṣṇa pakṣa ekādaśī", 42 | "27": "Kṛṣṇa pakṣa dvādaśī", 43 | "28": "Kṛṣṇa pakṣa trayodaśī", 44 | "29": "Kṛṣṇa pakṣa caturdasī", 45 | "30": "Amāvāsyā" 46 | }, 47 | 48 | "nakshatras": { "1":"Aśvinī", 49 | "2":"Bharaṇī", 50 | "3":"Kṛttikā", 51 | "4":"Rohiṇī", 52 | "5":"Mṛgaśirā", 53 | "6":"Ārdrā", 54 | "7":"Punarvasū", 55 | "8":"Puṣya", 56 | "9":"Āśleṣā", 57 | "10":"Maghā", 58 | "11":"Pūrvaphalgunī", 59 | "12":"Uttaraphalgunī", 60 | "13":"Hasta", 61 | "14":"Cittā", 62 | "15":"Svāti", 63 | "16":"Viśākhā", 64 | "17":"Anurādhā", 65 | "18":"Jyeṣṭhā", 66 | "19":"Mūlā", 67 | "20":"Pūrvāṣāḍhā", 68 | "21":"Uttarāṣāḍhā", 69 | "22":"Śravaṇā", 70 | "23":"Dhaniṣṭhā", 71 | "24":"Śatabhiṣā", 72 | "25":"Pūrvābhādrā", 73 | "26":"Uttarābhādrā", 74 | "27":"Revatī" 75 | }, 76 | 77 | "yogas": { 78 | "1":"Viṣkumbha", 79 | "2":"Prīti", 80 | "3":"Āyuṣmān", 81 | "4":"Saubhāgya", 82 | "5":"Śobhana", 83 | "6":"Atigaṇḍa", 84 | "7":"Sukarmā", 85 | "8":"Dhṛti", 86 | "9":"Śūla", 87 | "10":"Gaṇḍa", 88 | "11":"Vṛddhi", 89 | "12":"Dhruva", 90 | "13":"Vyāghāta", 91 | "14":"Harṣaṇa", 92 | "15":"Vajra", 93 | "16":"Siddhi", 94 | "17":"Vyatīpāta", 95 | "18":"Vārīyana", 96 | "19":"Parigha", 97 | "20":"Śiva", 98 | "21":"Siddha", 99 | "22":"Sādhya", 100 | "23":"Śubha", 101 | "24":"Śukla", 102 | "25":"Brahma", 103 | "26":"Aindra", 104 | "27":"Vaidhṛti" 105 | }, 106 | 107 | "karanas": { 108 | "1":"Kiṃstughna", 109 | "2":"Bava", 110 | "3":"Bālava", 111 | "4":"Kaulava", 112 | "5":"Taitila", 113 | "6":"Garaja", 114 | "7":"Vaṇija", 115 | "8":"Viṣṭi", 116 | "9":"Bava", 117 | "10":"Bālava", 118 | "11":"Kaulava", 119 | "12":"Taitila", 120 | "13":"Garaja", 121 | "14":"Vaṇija", 122 | "15":"Viṣṭi", 123 | "16":"Bava", 124 | "17":"Bālava", 125 | "18":"Kaulava", 126 | "19":"Taitila", 127 | "20":"Garaja", 128 | "21":"Vaṇija", 129 | "22":"Viṣṭi", 130 | "23":"Bava", 131 | "24":"Bālava", 132 | "25":"Kaulava", 133 | "26":"Taitila", 134 | "27":"Garaja", 135 | "28":"Vaṇija", 136 | "29":"Viṣṭi", 137 | "30":"Bava", 138 | "31":"Bālava", 139 | "32":"Kaulava", 140 | "33":"Taitila", 141 | "34":"Garaja", 142 | "35":"Vaṇija", 143 | "36":"Viṣṭi", 144 | "37":"Bava", 145 | "38":"Bālava", 146 | "39":"Kaulava", 147 | "40":"Taitila", 148 | "41":"Garaja", 149 | "42":"Vaṇija", 150 | "43":"Viṣṭi", 151 | "44":"Bava", 152 | "45":"Bālava", 153 | "46":"Kaulava", 154 | "47":"Taitila", 155 | "48":"Garaja", 156 | "49":"Vaṇija", 157 | "50":"Viṣṭi", 158 | "51":"Bava", 159 | "52":"Bālava", 160 | "53":"Kaulava", 161 | "54":"Taitila", 162 | "55":"Garaja", 163 | "56":"Vaṇija", 164 | "57":"Viṣṭi", 165 | "58":"Śakuni", 166 | "59":"Catuṣpāda", 167 | "60":"Nāgava" 168 | }, 169 | 170 | "varas": { 171 | "0": "Bhānuvāra", 172 | "1": "Somavāra", 173 | "2": "Maṅgalavāra", 174 | "3": "Budhavāra", 175 | "4": "Guruvāra", 176 | "5": "Śukravāra", 177 | "6": "Śanivāra" 178 | }, 179 | 180 | "samvats" : { 181 | "0":"Akṣaya", 182 | "1":"Prabhava", 183 | "2":"Vibhava", 184 | "3":"Śukla", 185 | "4":"Pramoda", 186 | "5":"Prajāpati", 187 | "6":"Āṅgirasa", 188 | "7":"Śrīmukha", 189 | "8":"Bhāva", 190 | "9":"Yuva", 191 | "10":"Dhātrī", 192 | "11":"Īśvara", 193 | "12":"Bahudhānya", 194 | "13":"Pramādhi", 195 | "14":"Vikrama", 196 | "15":"Vṛṣa", 197 | "16":"Citrabhānu", 198 | "17":"Svabhānu", 199 | "18":"Tāraṇa", 200 | "19":"Pārthiva", 201 | "20":"Vyaya", 202 | "21":"Sarvajit", 203 | "22":"Sarvadhārī", 204 | "23":"Virodhī", 205 | "24":"Vikṛti", 206 | "25":"Khara", 207 | "26":"Nandana", 208 | "27":"Vijaya", 209 | "28":"Jaya", 210 | "29":"Manmatha", 211 | "30":"Durmukhī", 212 | "31":"Hevilambī", 213 | "32":"Vilambī", 214 | "33":"Vikārī", 215 | "34":"Śārvarī", 216 | "35":"Plava", 217 | "36":"Śubhakṛt", 218 | "37":"Śobhakṛt", 219 | "38":"Krodhī", 220 | "39":"Viśvāvasu", 221 | "40":"Parābhava", 222 | "41":"Plavaṅga", 223 | "42":"Kīlaka", 224 | "43":"Saumya", 225 | "44":"Sādhāraṇa", 226 | "45":"Virodhikṛti", 227 | "46":"Paridhāvī", 228 | "47":"Pramādīca", 229 | "48":"Ānanda", 230 | "49":"Rākṣasa", 231 | "50":"Nala", 232 | "51":"Piṅgala", 233 | "52":"Kalāyukti", 234 | "53":"Siddhārthī", 235 | "54":"Raudra", 236 | "55":"Durmati", 237 | "56":"Dundubhi", 238 | "57":"Rudhirodgārī", 239 | "58":"Raktākṣī", 240 | "59":"Krodhana" 241 | }, 242 | 243 | "ritus": { 244 | "0": "Vasanta", 245 | "1": "Grīṣma", 246 | "2": "Varṣā", 247 | "3": "Śarad", 248 | "4": "Hemanta", 249 | "5": "Śiśira" 250 | }, 251 | 252 | "gauri": { "0": ["udyoga", "lābha", "viṣa", "amṛta", "śubha", "dhana", "amṛta", "viṣa", "śubha", "amṛta", "jvara", "roga", "kalaha", "lābha", "udyoga", "roga"], 253 | "1": ["amṛta", "viṣa", "udyoga", "jvara", "lābha", "amṛta", "lābha", "dhana", "roga", "lābha", "udyoga", "dhana", "roga", "amṛta", "viṣa", "jvara"], 254 | "2": ["roga", "udyoga", "jvara", "lābha", "amṛta", "udyoga", "viṣa", "lābha", "jvara", "udyoga", "kalaha", "lābha", "roga", "lābha", "udyoga", "dhana"], 255 | "3": ["viṣa", "amṛta", "jvara", "udyoga", "roga", "śubha", "dhana", "amṛta", "lābha", "roga", "viṣa", "udyoga", "śubha", "lābha", "dhana", "lābha"], 256 | "4": ["udyoga", "viṣa", "jvara", "lābha", "amṛta", "viṣa", "kalaha", "jvara", "sukha", "roga", "kalaha", "lābha", "udyoga", "jvara", "lābha", "udyoga"], 257 | "5": ["roga", "jvara", "amṛta", "kalaha", "lābha", "śubha", "dhana", "lābha", "amṛta", "jvara", "kalaha", "lābha", "śubha", "dhana", "amṛta", "viṣa"], 258 | "6": ["viṣa", "amṛta", "jvara", "udyoga", "śubha", "lābha", "dhana", "lābha", "viṣa", "udyoga", "śubha", "amṛta", "kalaha", "roga", "amṛta", "lābha"] 259 | }, 260 | 261 | // Must match the order of SE_SUN, SE_MOON, etc in swephexp.h 262 | // 9 = SE_PLUTO, but meh it's no longer a major planet. So, reassigning 9 = Ketu 263 | "planets": { "0": "sūrya", 264 | "1": "candra", 265 | "4": "maṅgala", 266 | "2": "budha", 267 | "5": "guru", 268 | "3": "śukra", 269 | "6": "śani", 270 | "10": "rāhu", 271 | "9": "ketu", 272 | "7": "harṣala", 273 | "8": "galla" 274 | }, 275 | 276 | // 0 = Aries, 1 = Taurus, etc 277 | "zodiac": { "0": "meṣa", 278 | "1": "vṛṣabha", 279 | "2": "mithuna", 280 | "3": "karkāṭaka", 281 | "4": "siṃha", 282 | "5": "kanyā", 283 | "6": "tulā", 284 | "7": "vṛścika", 285 | "8": "dhanus", 286 | "9": "makara", 287 | "10": "kumbha", 288 | "11": "mīna" 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /experimental/ayanamsa_yoc.c: -------------------------------------------------------------------------------- 1 | // Ayanamsha Time of Coincidence calculator 2 | // 3 | // Finds the zero point of the sidereal ecliptic for a given type of ayanamsha 4 | // All dates are (proleptic) Gregorian calendar 5 | // 6 | // Tested with SwissEph 2.02.01 7 | // gcc $CPPFLAGS $LIBS -lm -lswe file.c 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef SE_SIDM_TRUE_CITRA 16 | #define SE_SIDM_TRUE_CITRA 27 17 | #endif 18 | 19 | #ifndef SE_SIDM_TRUE_REVATI 20 | #define SE_SIDM_TRUE_REVATI 28 21 | #endif 22 | 23 | #ifndef SE_SIDM_TRUE_PUSHYA 24 | #define SE_SIDM_TRUE_PUSHYA 29 25 | #endif 26 | 27 | // normalize angle to lie in range [-180, 180) degrees 28 | double wrap180(double angle) 29 | { 30 | assert(angle >= 0 && angle <= 360); 31 | return angle > 180 ? angle - 360 : angle; 32 | } 33 | 34 | void to_dms(double deg) 35 | { 36 | deg = wrap180(deg); 37 | int d = deg; 38 | double mins = ((deg - d) * 60); 39 | int m = mins; 40 | double s = (mins - m) * 60; 41 | printf("%03d:%02d:%011.8lf\n", d, m, s); 42 | } 43 | 44 | void galactic_center(void) 45 | { 46 | swe_set_ephe_path("/opt/packages/swisseph/ephe"); //better setenv SE_EPHE_PATH 47 | double julday = swe_julday(550, 3, 9, 14 + 3/60. + 32.45/3600, SE_GREG_CAL); 48 | swe_set_sid_mode(SE_SIDM_USER, julday, 0.0); 49 | 50 | double position[10] = {0}; // longitude, latitude, distance, speed, etc 51 | char errmsg[512] = {0}; 52 | char star[50]; strcpy(star, "Gal. Center"); 53 | int errcode = swe_fixstar_ut(star, 54 | julday, 55 | SEFLG_SIDEREAL | SEFLG_SWIEPH, 56 | position, 57 | errmsg); 58 | to_dms(fmod(position[0], 30)); // prints 006:39:59.99998588 in 2.02 59 | // prints 006:40:0.00476062 in 1.80 60 | 61 | // As claimed in Swisseph for SS_REVATI 62 | julday = swe_julday(499, 3, 21, 7 + 30/60. + 21.57/3600, SE_GREG_CAL); 63 | swe_set_sid_mode(SE_SIDM_USER, julday, -0.79167046); 64 | strcpy(star, ",zePsc"); // Revati = Zeta Piscium (see fixstars.cat) 65 | errcode = swe_fixstar_ut(star, 66 | julday, 67 | SEFLG_SIDEREAL | SEFLG_SWIEPH, 68 | position, 69 | errmsg); 70 | printf("SS_REVATI_UT = %.8lf\n", position[0]); 71 | // Surya Siddhanta claims this is 359°50' but returns 359°43'18.4" 72 | 73 | // True_Revati of Swiss Eph puts it at 0° always 74 | swe_set_sid_mode(SE_SIDM_TRUE_REVATI, 0, 0); 75 | errcode = swe_fixstar_ut(star, 76 | julday, 77 | SEFLG_SIDEREAL | SEFLG_SWIEPH, 78 | position, 79 | errmsg); 80 | printf("True_Revati_UT = %.8lf\n", position[0]); // prints 0.00000000 81 | 82 | // TRUE_REVATI As discovered by me 83 | julday = swe_julday(563, 7, 20, 19 + 16/60. + 2.17/3600, SE_GREG_CAL); 84 | swe_set_sid_mode(SE_SIDM_USER, julday, 0.0); 85 | errcode = swe_fixstar_ut(star, 86 | julday, 87 | SEFLG_SIDEREAL | SEFLG_SWIEPH, 88 | position, 89 | errmsg); 90 | printf("Revati_UT @ 390°50' = %.8lf\n", position[0]); 91 | // prints 359.83333333 == 359°50'00' exactly 92 | } 93 | 94 | typedef double (*func1_t)(double); 95 | 96 | double get_star_position(const char* star_, double point) 97 | { 98 | double position[10] = {0}; // longitude, latitude, distance, speed, etc 99 | char errmsg[512] = {0}; 100 | char star[50]; 101 | int errcode; 102 | 103 | swe_set_sid_mode(SE_SIDM_USER, point, 0.0); 104 | 105 | strcpy(star, star_); 106 | 107 | // (not fixstar_ut) 108 | errcode = swe_fixstar(star, 109 | point, 110 | SEFLG_SIDEREAL | SEFLG_SWIEPH, 111 | position, 112 | errmsg); 113 | 114 | if (errcode == 0) { printf("%s: %s\n", __func__, errmsg); } 115 | return position[0]; 116 | } 117 | 118 | double revati(double point) 119 | { 120 | double fval = 0.0; 121 | 122 | // Place Revati at at 359°50' 123 | fval = wrap180(get_star_position("Revati", point)) - ((359 + 50/60.) - 360); 124 | return fval; 125 | } 126 | 127 | double gal_cent(double point) 128 | { 129 | double fval = 0.0; 130 | 131 | // Place Galactic Center at middle of Mula (246°40') 132 | fval = get_star_position("Gal. Center", point) - (246 + 40/60.); 133 | return fval; 134 | } 135 | 136 | double ayan_func(double x) 137 | { 138 | return wrap180(swe_get_ayanamsa(x)); 139 | } 140 | 141 | // Find 'x' in range [start, stop] such that func(x) == 0.00 142 | double bisection_search(func1_t func, double start, double stop) 143 | { 144 | double left = start; 145 | double right = (stop > 0) ? stop : 2500000; // JD = 31 Aug 2132 146 | double epsilon = 5E-10; 147 | 148 | do { 149 | register double middle = (left + right) / 2.; 150 | register double midval = func(middle); 151 | register double rtval = func(right); 152 | 153 | if (midval * rtval >= 0.0) { 154 | right = middle; 155 | } else { 156 | left = middle; 157 | } 158 | 159 | } while (right - left > epsilon); 160 | 161 | return (right + left) / 2; 162 | } 163 | 164 | void ss_citra(void) 165 | { 166 | swe_set_sid_mode(SE_SIDM_SS_CITRA, 0, 0); 167 | 168 | double dhour = 7 + 30/60. + 31.57/3600; 169 | double tjd_ut = swe_julday(499, 3, 21, dhour, SE_JUL_CAL); 170 | double daya = swe_get_ayanamsa_ut(tjd_ut); 171 | double daya_tt = swe_get_ayanamsa(tjd_ut); 172 | 173 | printf("\njd(UT) = %.14lf, ayanamsha = %.15lf, (using wrong function = %.15lf)\n", 174 | tjd_ut, daya, daya_tt); 175 | 176 | } 177 | 178 | void date_conversion(void) 179 | { 180 | int year, month, day, hour, min; 181 | double sec, timezone = 5.5; 182 | 183 | /* Local time to UTC. Enter +5.5 */ 184 | swe_utc_time_zone(1985, 6, 9, 9, 40, 0, timezone, 185 | &year, &month, &day, &hour, &min, &sec); 186 | 187 | printf("%d-%d-%d %d:%d:%lf\n", year, month, day, hour, min, sec); 188 | 189 | /* UTC to JD(UT1) */ 190 | double jd[2]; 191 | swe_utc_to_jd(year, month, day, hour, min, sec, SE_GREG_CAL, jd, NULL); 192 | printf("JD(ET) = %lf, JD(UT1) = %lf\n", jd[0], jd[1]); 193 | 194 | double djd = swe_julday(1985, 6, 9, 9 + 40/60., SE_GREG_CAL) - timezone/24.; 195 | double dt = swe_deltat(djd); 196 | double dut = djd + dt; 197 | printf("Using Julday (wrrooong): %lf\n", dut); 198 | 199 | /* JD(UT1) to UTC */ 200 | swe_jdut1_to_utc(jd[1], SE_GREG_CAL, &year, &month, &day, &hour, &min, &sec); 201 | printf("UTC: %d-%d-%d %d:%d:%lf\n", year, month, day, hour, min, sec); 202 | 203 | /* UTC to local time zone. India is +5.5 but for UTC -> local, enter -5.5 */ 204 | int y, m, d, h, mi; 205 | double dsec; 206 | swe_utc_time_zone(year, month, day, hour, min, sec, -timezone, 207 | &y, &m, &d, &h, &mi, &dsec); 208 | printf("Local: %d-%d-%d %d:%d:%lf\n", y, m, d, h, mi, dsec); 209 | } 210 | 211 | int main(int argc, char* argv[]) 212 | { 213 | double start = swe_julday(-100, 1, 1, 0, SE_GREG_CAL); // 1 Jan 100 BCE 214 | double end = swe_julday(2100, 1, 1, 0, SE_GREG_CAL); // 1 Jan 2100 CE 215 | 216 | const int c_ayanamsa_list[] = 217 | { SE_SIDM_FAGAN_BRADLEY, 218 | SE_SIDM_LAHIRI, 219 | SE_SIDM_DELUCE, 220 | SE_SIDM_RAMAN, 221 | SE_SIDM_USHASHASHI, 222 | SE_SIDM_KRISHNAMURTI, 223 | SE_SIDM_DJWHAL_KHUL, 224 | SE_SIDM_YUKTESHWAR, 225 | SE_SIDM_JN_BHASIN, 226 | SE_SIDM_BABYL_KUGLER1, 227 | SE_SIDM_BABYL_KUGLER2, 228 | SE_SIDM_BABYL_KUGLER3, 229 | SE_SIDM_BABYL_HUBER, 230 | SE_SIDM_BABYL_ETPSC, 231 | SE_SIDM_ALDEBARAN_15TAU, 232 | SE_SIDM_HIPPARCHOS, 233 | SE_SIDM_SASSANIAN, 234 | SE_SIDM_GALCENT_0SAG, 235 | SE_SIDM_J2000, 236 | SE_SIDM_J1900, 237 | SE_SIDM_B1950, 238 | SE_SIDM_SURYASIDDHANTA, 239 | SE_SIDM_SURYASIDDHANTA_MSUN, 240 | SE_SIDM_ARYABHATA, 241 | SE_SIDM_ARYABHATA_MSUN, 242 | SE_SIDM_SS_REVATI, 243 | SE_SIDM_SS_CITRA, 244 | SE_SIDM_TRUE_CITRA, 245 | SE_SIDM_TRUE_REVATI, 246 | SE_SIDM_TRUE_PUSHYA }; 247 | 248 | int num = (argc > 1) ? atoi(argv[1]) : 30; 249 | if (num > SE_NSIDM_PREDEF) { printf("%d: Out of range!\n", num); exit(1); } 250 | 251 | int ayanamsa_list[num]; 252 | for (int i = 0; i < num; i++) { ayanamsa_list[i] = c_ayanamsa_list[i]; } 253 | 254 | double zero_points[num]; 255 | int year, month, day; 256 | double hours; 257 | 258 | for (int i = 0; i < num; i++) { 259 | int ayan = ayanamsa_list[i]; 260 | printf("%02d, ", ayan); 261 | printf("name = %-30s, ", swe_get_ayanamsa_name(ayan)); 262 | 263 | swe_set_sid_mode(ayan, 0, 0); 264 | zero_points[i] = bisection_search(ayan_func, start, end); 265 | 266 | printf("julday = %0.8lf, ", zero_points[i]); 267 | 268 | swe_revjul(zero_points[i], SE_GREG_CAL, &year, &month, &day, &hours); 269 | printf("year = %+05d, month = %02d, day = %02d, hour = ", year, month, day); 270 | to_dms(hours); 271 | } 272 | 273 | for (int i = 0; i < num; i++) { 274 | int sid = ayanamsa_list[i]; 275 | printf("%02d. ", sid); 276 | printf("name = %-30s, ", swe_get_ayanamsa_name(sid)); 277 | swe_set_sid_mode(sid, 0, 0); 278 | 279 | double jd = 1927135.8747793; 280 | double ayan = swe_get_ayanamsa_ut(jd); 281 | to_dms(ayan); 282 | } 283 | galactic_center(); 284 | 285 | ss_citra(); 286 | 287 | // Fixed point of Revati@359°50 288 | double revati_359_50 = bisection_search(revati, start, end); 289 | printf("JD = %.9lf, Revati position: %.9lf\n", 290 | revati_359_50, 291 | get_star_position("Revati", revati_359_50)); 292 | 293 | // Fixed point of Gal cent at mid-mula 294 | double gal_cent_mula = bisection_search(gal_cent, start, end); 295 | printf("JD = %.9lf, Gal center position: %.9lf\n", 296 | gal_cent_mula, 297 | get_star_position("Gal. Center", gal_cent_mula)); 298 | 299 | date_conversion(); 300 | } 301 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Drik Panchanga 2 | ============== 3 | 4 | Observational Indian lunisolar calendar using the Swiss ephemeris (Hindu 5 | Drig-ganita Panchanga). 6 | 7 | Features 8 | -------- 9 | 10 | Computation of the five essentials of the panchangam: 11 | * Tithi 12 | * Nakshatra 13 | * Yoga 14 | * Karana 15 | * Vaara 16 | 17 | Not just the values, but also the end times of tithis and nakshatras 18 | are computed. The only factor limiting the accuracy of the program 19 | output is the uncertainity in your input values (latitude, longitude). 20 | 21 | Also includes computation of sunrise, sunset, moonrise and moonset. 22 | 23 | Included in the CLI version (not yet in GUI): 24 | * Instantaneous planetary positions, including Lagna (Ascendant) 25 | * Navamsa positions 26 | * Choghadiya/Gauri panchanga 27 | * Vimsottari Dasha-Bhukti 28 | * Rahu Kala, Yamaganda Kala, Gulika Kala 29 | * Abhijit muhurta and Durmuhurtams 30 | 31 | 32 | By default, the month type is Amavasyanta (new moon to new moon) which 33 | is most prominent type of calendar used in South India. 34 | 35 | NOTE: 36 | All timings are end timings. Timings displayed higher than 24:00 denote 37 | hours past midnight because the Hindu day (tithi) starts and ends with 38 | sunrise. If applicable, daylight savings (DST) are accounted for 39 | automatically based on the date and place entered in the textboxes. 40 | 41 | 42 | Requirements 43 | ------------ 44 | 45 | Python interface to Swiss ephemeris. 46 | ``` 47 | pip install pyswisseph # OR apt-get install pyswisseph 48 | ``` 49 | The core of the library (`panchanga.py`) can be imported into other code 50 | or used from the command line. 51 | 52 | In order to just _run_ the GUI (`gui.py`) you also need python-tz and 53 | wxPython (interface to wxWidgets): 54 | ``` 55 | apt-get install python-tz 56 | apt-get install python-wxgtk3.0 57 | ``` 58 | 59 | If you want to _modify_ the GUI (`Gui.wxg`), you must use wxGlade: 60 | ``` 61 | apt-get install python-wxglade 62 | ``` 63 | 64 | Wxglade 0.7.0 is buggy (0.6.8 is ok), try the development version from [here][wxgde]. 65 | 66 | [wxgde]: https://bitbucket.org/agriggio/wxglade/downloads 67 | 68 | How does it look? 69 | 70 | [Sample screenshot](https://raw.githubusercontent.com/bdsatish/drik-panchanga/master/screenshot.jpg): 71 | 72 | ![Sample screenshot](https://raw.githubusercontent.com/bdsatish/drik-panchanga/master/screenshot.jpg "Hindu Panchanga") 73 | 74 | Using the GUI 75 | ------------- 76 | 77 | ### Location known 78 | 79 | First, type the Date in DD/MM/YYYY format in the 'Date' field. Negative value for YYYY are 80 | interpolated as proleptic Gregorian calendar. 81 | 82 | Second, type your location (city or district) in the Location field and click 'Search'. If found, 83 | then the coordinates and time zone are updated. If not, try the [next method](#location-unknown). 84 | If your location's population is more than 50,000 then the location should be found. 85 | 86 | Third, click 'Compute'. Now the fields like tithi, etc. are computed and shown on the GUI. 87 | 88 | ### Location unknown 89 | 90 | First, type the Date in DD/MM/YYYY format in the 'Date' field. 91 | 92 | Second, manually enter the coordinates and time zone of your location. You can use 93 | [Google Maps](http://maps.google.com) or [Time and Date website](http://www.timeanddate.com/) for 94 | this purpose. 95 | 96 | Third, click 'Compute'. Now the fields like tithi, etc. are computed and shown on the GUI. 97 | 98 | 99 | Accuracy 100 | -------- 101 | 102 | The program is as accurate as the Swiss Ephemeris installed on your system. So generally it is 103 | accurate for years 5000 BCE to 5000 CE, especially in the range 2500 BCE - 2500 CE. The 104 | computational speed stays the same no matter which date you enter. Compared to other software listed 105 | in the [References](#references), our software is way better in this sense. 106 | 107 | As a simple test, try to compute the date of Madhva Navami, which is celebrated as the disappearance 108 | day of the Indian philosopher [Madhvācārya](http://en.wikipedia.org/wiki/Madhvacharya). The exact 109 | date is 1317 CE, Māgha-māsa śukla-pakṣa navamī. All other software listed in 110 | [References](#references) give error "Year out-of-range". But in our software, enter the place 111 | "Udipi" and date "30/1/1317" and you indeed get Māgha śuddha navamī. You can cross-verify it on the 112 | [Calendrica website](http://emr.cs.iit.edu/home/reingold/calendar-book/Calendrica.html). 113 | 114 | Note that dates before 1582 must be entered in 115 | [proleptic Gregorian](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar), which is a 116 | natural back-interpolation of the current Gregorian calendar we use everyday. 117 | 118 | 119 | About the calendar 120 | ------------------ 121 | 122 | There are two schools of Indian calendar makers: 123 | 124 | 1. Those who follow the rules of the [_Sūrya Siddhāntā_](http://en.wikipedia.org/wiki/Surya_Siddhanta) 125 | (SS, Theory of the Sun) or its variants like _Ārya Siddhānta_ of Aryabhata. 126 | 2. Those who follow the _Dṛk Siddhāntā_ (Empirical Theory). 127 | 128 | SS contains semi-analytical equations for specifying the positions of sun and moon. 129 | However, the constants in these equations have to be updated regularly ( _bīja saṃskāra_ ). 130 | But the equations in SS were last updated around 1000 CE, so they no longer match the 131 | planetary positions as we see today. For example, the date of solar eclipse as predicted 132 | by the equations of SS are off by many hours from its actual occurence. In spite of this, 133 | most Hindu maṭhas still publish yearly pañcāṅgas according to the rules of SS, in the name 134 | of preserving and practising tradition ( _paramparā_ ). 135 | 136 | The latter one, _Drik_ school, still follow the general concepts from SS, 137 | but get the planetary positions from measured or observed data (dṛś = to see). 138 | Hence, their results match accurately with observed celestial phenomena. 139 | The [Swiss Ephemeris](http://www.astro.com/swisseph/swephinfo_e.htm) is probably 140 | the best source available today for planetary calculations. It provides highly 141 | accurate databases of planetary data for about 10000 years. Hence, this panchanga 142 | is based on the Swiss Ephemeris. Other databases include those published by NASA's 143 | JPL (DE405) or the Moshier ephemeris. 144 | 145 | #### Śubhāśubha Samaya 146 | 147 | Gaurī (Gowri) Panchanga and Choghadiya are south Indian and north Indian names 148 | respectively, for the same mathematical calculation. Basically, day and night 149 | duration are each divided into eight parts; the difference between N.Indian and 150 | S.Indian lies in their names and which part is considered 151 | auspicious/inauspicious. In the S.Indian variant, Tamilians use 152 | [different order][kowri] and names compared to Kannadigas/Telugus. This program 153 | provides the latter only. (This inconsitency alone is enough to let you know 154 | that such concepts of (in-)auspiciousness are all pseudo-science). 155 | 156 | Rāhukāla, Yamagaṇḍakāla, Gulikakāla, Durmuhūrtams and Varjyam are all considered 157 | inauspicious. Abhijit muhūrta and Amṛtakāla are considered auspicious. 158 | 159 | [kowri]: http://tamilastrology.hosuronline.com/KowriPanchangam/ 160 | 161 | ### Uranus and Neptune 162 | 163 | These planets were not discovered by Indian astronomers. They are sometimes 164 | translated as "[Aruṇa graha][ar_hi]" and "[Varuṇa graha][va_hi]" in languages 165 | like Hindi, Nepali, etc. Problem is that there is another trans-Neptunian 166 | planet which is also called [Varuna][v20k] in English. 167 | 168 | The Positional Astronomy Center [translates][pac] them as `हर्शल` and 169 | `नेपच्यून`. This is inconsistent in the sense that Uranus was translated after its 170 | discoverer (William Herschel) where as Neptune was phonetically transcribed from 171 | English, instead of basing on its discoverer (Johann Galle). Therefore, I've 172 | "Indianized" their names in a rhyming fashion as **`हर्षल`** (=Uranus) and 173 | **`गल्ल`** (=Neptune). They also mean "happy" and "cheek/chin" respectively in 174 | many Indian languages. 175 | 176 | [ar_hi]: https://hi.wikipedia.org/wiki/अरुण_(ग्रह) 177 | [va_hi]: https://hi.wikipedia.org/wiki/वरुण_(ग्रह) 178 | [pac]: http://www.packolkata.gov.in/download/hindi/Page_020.jpg 179 | [v20k]: https://en.wikipedia.org/wiki/20000_Varuna 180 | 181 | References 182 | ---------- 183 | 184 | These ones are helpful for implementing panchanga software: 185 | * Karanam Ramakumar, [_Panchangam Calculations_](http://archive.org/details/PanchangamCalculations) 186 | * [_Second Level of the Astronomical Calculations in GCAL_](http://www.krishnadays.com/eng/index.php?option=com_docman&task=doc_download&gid=7&Itemid=58), 187 | used in ISKCON's GCal software. 188 | 189 | This is _the_ calendar book (though it mostly deals with Surya Siddhanta): 190 | * Dershowitz and Reingold, _Calendrical Calculations_, 3rd edition, 2008. 191 | [Online Java applet](http://emr.cs.iit.edu/home/reingold/calendar-book/Calendrica.html). 192 | 193 | * Shayamasundara Dasa, [_Vimsottari Year -- 360 or 365 ?_](http://shyamasundaradasa.com/jyotish/resources/articles/pdf_versions/english/360_vs_365.pdf) 194 | 195 | #### Similar software #### 196 | 197 | Prof. M. Yanom's [online interface](http://www.cc.kyoto-su.ac.jp/~yanom/pancanga/) 198 | to his [Perl code](http://www.cc.kyoto-su.ac.jp/~yanom/sanskrit/pancanga/pancanga3.13) -- this 199 | is the best version of the old Surya Siddhanta pancanga I've seen. However, the Surya Siddhanta 200 | system (no fault with the Perl code) is not accurate if you want to work with dates which are 201 | several centuries before our current time. 202 | 203 | [drikpanchang](http://drikpanchang.com) is a reliable online calendar for the Drik. However, it is 204 | neither open source nor do they have a desktop program. This website doesn't work for dates before 205 | 1600 CE. Their [Android app](https://play.google.com/store/apps/details?id=com.drikp.core) doesn't 206 | work for dates outside the range 1900 - 2100 CE. 207 | 208 | 209 | [Hindu Calendar](https://play.google.com/store/apps/details?id=com.alokmandavgane.hinducalendar) 210 | for Android is another offline Drik calendar by Alok Mandavgane. Again, this is 211 | not open source. This software has a bug that it doesn't account for daylight savings 212 | in Europe. Also it doesn't work for dates outside the range 1900 - 2100 CE. 213 | 214 | Among open source programs, I found these two: 215 | 216 | * [On Google Code](http://panchangam.googlecode.com/svn/calc-v2): generates a pdf of 217 | panchanga for any year and place, but imprecise. For ex., tithi end timings are off 218 | by ten minutes sometimes. There is no GUI either. 219 | 220 | * [On GitHub](https://github.com/santhoshn/panchanga): Based on Paul Schlyter's 221 | semi-analytical model for [planetary positions](http://stjarnhimlen.se/comp/ppcomp.html). 222 | This program gives the panchanga for a given _instant_ but doesn't ask for a place's 223 | coordinates or timezone. This is probably because the program doesn't compute sunrise 224 | timings at all! The planetary model fails for dates outside the range 1800 CE to 2100 CE. 225 | 226 | 227 | License 228 | ------- 229 | 230 | GNU Affero GPL version 3 (or later). 231 | 232 | 233 | #### TODO #### 234 | 235 | * Festivals 236 | * Amritakala, varjyam 237 | * gettext translations 238 | * Harmonize all functions to use UT aka UT1 instead of UTC or ET 239 | (`swe.jdut1_to_utc() <==> swe.utc_to_jd()[1]`, `swe.utc_time_zone()`, etc.) 240 | -------------------------------------------------------------------------------- /experimental/ayanāmśa.md: -------------------------------------------------------------------------------- 1 | # About different ayanamshas 2 | 3 | From the [Swiss ephemeris documentation][1] 4 | 5 | We have found that there are basically three definitions, not counting the 6 | manifold variations: 7 | 8 | 1. the Babylonian zodiac with Spica at 29 Virgo or Aldebaran at 15 Taurus: 9 | a) P. Huber, b) Fagan/Bradley c) refined with Aldebaran at 15 Tau 10 | 2. the Greek-Arabic-Hindu zodiac with the zero point between 10 and 20 east of zeta Piscium: 11 | a) Hipparchus, b) Ushâshashî, c) Sassanian 12 | 3. the Greek-Hindu astrological zodiac with Spica at 0 Libra 13 | a) Lahiri 14 | 15 | The differences are: 16 | between 1) and 3) is about 1 degree 17 | between 1) and 2) is about 5 degrees 18 | between 2) and 3) is about 4 degrees 19 | 20 | The zero points of all of these are around 560 AD: 21 | 22 | ``` 23 | 04. name = Ushashashi , julday = 1925433.8940655 year = +0559, month = 07, day = 23, hour = 009:27:27.25914001 24 | 15. name = Hipparchos , julday = 1920430.0933129 year = +0545, month = 11, day = 09, hour = 014:14:22.23971397 25 | 16. name = Sassanian , julday = 1927135.8163668 year = +0564, month = 03, day = 20, hour = 007:35:34.09921378 26 | 22. name = Suryasiddhanta, mean Sun, julday = 1909045.5186681 year = +0514, month = 09, day = 09, hour = 000:26:52.92675734 27 | 24. name = Aryabhata, mean Sun , julday = 1909650.7495622 year = +0516, month = 05, day = 06, hour = 005:59:22.17763424 28 | 25. name = SS Revati , julday = 1924230.2014096 year = +0556, month = 04, day = 05, hour = 016:50:1.79022253 29 | 26. name = SS Citra , julday = 1847826.9474462 year = +0347, month = 01, day = 29, hour = 010:44:19.35084075 30 | ``` 31 | 32 | Ushashashi (Indian), Hipparchos (Greek), Sassanian (Arabic) and SS Revati 33 | (Suryasiddhanta) are all based on Revati. The rest are based on Citra. 34 | 35 | 36 | ``` 37 | SS_REVATI: The Suryasiddhanta also mentions that Revati/zeta-Piscium is 38 | exactly at 359°50’ in polar ecliptic longitude (projection onto the ecliptic 39 | along meridians). Therefore the following two ayanamshas were added: 40 | 41 | 25) ayanamsha = -0.79167046 21 Mar 499, 7:30:31.57 UT = noon at Ujjain, 75.7684565 E. 42 | Revati/zePsc at polar ecliptic longitude 359°50’ 43 | 44 | 45 | SS_CITRA: The Suryasiddhanta gives Spica the position 180° in polar longitude 46 | (ecliptic longitude, but projection on meridian lines). From this, the following 47 | Ayanamsha can be derved: 48 | 49 | 26) ayanamsha = 2.11070444 21 Mar 499, 7:30:31.57 UT = noon at Ujjain, 75.7684565 E. 50 | Citra/Spica at polar ecliptic longitude 180°. 51 | 52 | ``` 53 | 54 | `TRUE_CITRA`, `TRUE_REVATI` and `TRUE_PUSHYA` give same constant value of their 55 | ayanamsa. They do not change w.r.t julian date, unlike others: 56 | 57 | ``` 58 | 27. name = True Citra 3°52’15.85906894” 59 | 28. name = True Revati 0°00’00.00000000” 60 | 29. name = True Pushya -106°00’00.00000000” 61 | ``` 62 | 63 | 64 | ```python 65 | import swisseph as swe 66 | jd = 1927135.8747793 # 18 Mar 564 67 | 68 | swe.set_sid_mode(swe.SIDM_TRUE_REVATI) 69 | to_dms(swe.get_ayanamsa_ut(jd)) 70 | 71 | [0, 0, 0.0] 72 | 73 | swe.set_sid_mode(swe.SIDM_TRUE_CITRA) 74 | to_dms(swe.get_ayanamsa_ut(jd)) 75 | [3, 52, 15.859]) 76 | 77 | ``` 78 | 79 | Among the Indian/Hindu traditions for calculating Ayanamsha, the Lahiri system 80 | is based on Citra (Spica). For the above date, some notable values: 81 | 82 | ``` 83 | # All these are > 1° 84 | ('Fagan/Bradley', 0, [4, 44, 51.221]) 85 | ('Lahiri', 1, [3, 51, 51.136]) 86 | ('Krishnamurti', 5, [3, 46, 3.406]) 87 | ('SS Citra', 26, [3, 0, 46.101]) 88 | ('True Citra', 27, [3, 52, 15.859]) 89 | 90 | # All these are [0°, 1°) 91 | ('Ushashashi', 4, [0, 3, 52.862]) 92 | ('Suryasiddhanta', 21, [0, 54, 7.565]) 93 | ('Suryasiddhanta, mean Sun', 22, [0, 41, 14.883]) 94 | ('Aryabhata', 23, [0, 54, 7.568]) 95 | ('Aryabhata, mean Sun', 24, [0, 39, 52.091]) 96 | ('SS Revati', 25, [0, 6, 37.551]) 97 | ('True Revati', 28, [0, 0, 0.0]) 98 | ``` 99 | 100 | The closest value among Indian traditions is that of Uṣāśaśi, with 101 | 0°3′53″. Interestingly, the Greek and Arabic traditions also predict a value 102 | close to zero for the same date: 103 | 104 | ``` 105 | ('Hipparchos', 15, [0, 15, 17.46]) 106 | ('Sassanian', 16, [0, 0, 0.008]) 107 | ``` 108 | 109 | In a sense, revatīpakṣa ayanāmśa has a remarkable Greek-Arabic-Indian 110 | synchronism. 111 | 112 | The Hipparchan ayanamsha is -9°20′ as on 27 June –128 (jd 1674484). 113 | 114 | UshaShashi reaches zero point at 23 Jul 559 CE, 09:27 115 | 116 | ```python 117 | for i in range(0, 29): 118 | swe.set_sid_mode(i) 119 | name = swe.get_ayanamsa_name(i) if i != 28 else 'True Revati' 120 | print(name, i, to_dms(swe.get_ayanamsa_ut(swe.julday(559, 7, 23, 9.46)))) 121 | ``` 122 | 123 | [1]: http://www.astro.com/swisseph/swisseph.htm 124 | 125 | ## Galactic Center and Mūlā Nakṣatra 126 | 127 | We can choose the zero-point of the ayanamsa to coincide exactly with the 128 | beginning of Sagittarius/Dhanus constallation (which spans from 240° to 129 | 270°). This is what Swiss eph's `swe.SIDM_GALCENT_0SAG` does: 130 | 131 | ``` 132 | swe.set_sid_mode(swe.SIDM_GALCENT_0SAG) 133 | julday = 1746447.4042490 # 04 Jul, 69 CE 134 | swe.fixstar_ut("Gal. Center", julday, flag = swe.FLG_NOABERR | swe.FLG_SIDEREAL) 135 | (240.00133698938166, -5.349660021998989, 0.9999999999999999, 0.0, 0.0, 0.0) 136 | ``` 137 | where the longitude is `0° Sag 0’ 4.8132”`. If you get the Ayanamsa on that day, 138 | it is indeed zero: 139 | 140 | ``` 141 | swe.get_ayanamsa_ut(julday) 142 | 2.839101398421917e-09 143 | ``` 144 | 145 | There are 27 Nakshatras spanning 360° so each occupies 13°20’ exactly. The 30° 146 | of Sagittarius is spanned by Mūlā Nakṣatra (0° to 13°20’), Pūrvāṣāḍhā (13°20’ to 147 | 26°40’), and Uttārāṣāḍhā 1st pādā (26°40’ to 30°). The above mode puts the 148 | ayanamsa beginning at 0° Sag = 0° of Mūlā. However, people claim UshaShashi puts 149 | the galactic center at the _middle_ of Mūlā instead of _beginning_. 150 | 151 | ``` 152 | swe.set_sid_mode(swe.SIDM_USHASHASHI) 153 | julday = 1925433.8940655 # Zeropoint of Ayanamsa 154 | swe.get_ayanamsa_ut(julday) 155 | 1.5592931390528975e-09 156 | galc = swe.fixstar_ut("Gal. Center", julday, flag = swe.FLG_NOABERR | swe.FLG_SIDEREAL) 157 | to_dms_prec(galc[0] % 30) 158 | [6, 47, 44.2442] 159 | ``` 160 | 161 | The middle of Mūlā is `6°40’` but the above is off by 7’44” :( 162 | 163 | Repeating the procedure for other types of Ayanamsas: 164 | 165 | ``` 166 | Sagittarius Sagittarius 167 | (with FLG_NOABERR) (without FLG_NOABERR) 168 | GALACTIC_CENTER [0, 0, 4.8132] [0, 0, 19.7637] 169 | 170 | SS_REVATI [6, 44, 59.5756] [6, 45, 12.2397] 171 | USHASHASHI [6, 47, 44.2442] [6, 47, 56.4404] 172 | SASSANIAN [6, 51, 37.0657] [6, 51, 44.8046] 173 | HIPPARCHAN [6, 36, 19.763] [6, 35, 59.9548] 174 | 175 | LAHIRI [2, 59, 47.7086] [2, 59, 44.6748] 176 | FAGAN_BRADLEY [2, 6, 48.0358] [2, 6, 28.4751] 177 | SS_CITRA [3, 50, 52.4752] [3, 50, 43.5064] 178 | YUKTESHWAR [4, 22, 28.7773] [4, 22, 17.8058] 179 | ``` 180 | 181 | The correct value is `6°40’`, SS_REVATI is the closest. 182 | 183 | In fact, we can calculate our own Ayanamsa (by bisection root-finding) to 184 | coincide with the middle of Mūlā: 185 | 186 | ``` 187 | julday = swe.julday(550, 3, 9, 14 + 3/60. + 32.45/3600.) 188 | swe.set_sid_mode(swe.SIDM_USER, julday, 0) # we assert that ayanamsa(julday) == 0 189 | galc = swe.fixstar_ut("Gal. Center", julday, flag = swe.FLG_SIDEREAL); to_dms_prec(galc[0] % 30) 190 | [6, 40, 0.0] 191 | ``` 192 | 193 | So, choosing the zero-point of Ayanamsa on 9 Mar 550 CE at 14:03:32.45 UTC 194 | (JD 1922011.0857922453) results in placing the Galactic Center at the 195 | middle of Mūlā nakshatra (6°40’). 196 | 197 | [This post from Ernst Wilhelm][2] suggests to set the middle-Mula-ayanamsa to 198 | 20°11'11" on 1/1/2000. This also results in 6°39.5’ 199 | 200 | ``` 201 | julday = swe.julday(2000, 1, 1) 202 | swe.set_sid_mode(swe.SIDM_USER, julday, 20 + 11/60. + 11./3600) 203 | galc = swe.fixstar_ut("Gal. Center", julday, flag = swe.FLG_SIDEREAL); to_dms_prec(galc[0] % 30) 204 | [6, 39, 34.830456] 205 | ``` 206 | 207 | ## TRUE_CITRA and TRUE_REVATI 208 | 209 | ### Revati at 359°50’ 210 | 211 | The Suryasiddhanta also mentions that Revati/zeta-Piscium is exactly at 359°50’ 212 | in polar ecliptic longitude (projection onto the ecliptic along 213 | meridians). So, according to SS_REVATI: 214 | 215 | ``` 216 | jd = swe.julday(499, 3, 21, 7 + 30/60. + 21.57/3600) 217 | swe.set_sid_mode(swe.SIDM_USER, jd, -0.79167046) 218 | galc = swe.fixstar_ut("Revati", jd, flag = swe.FLG_SIDEREAL) 219 | to_dms_prec(galc[0]) 220 | [359, 43, 18.397513] 221 | ``` 222 | 223 | which is off by 7'18.4". Once again, we can do bisection search to find the 224 | zero-point of Revati@359°50’: 225 | 226 | ``` 227 | jd = swe.julday(563, 7, 20, 19 + 16/60. + 2.17/3600) 228 | swe.set_sid_mode(swe.SIDM_USER, jd, 0.0) 229 | galc = swe.fixstar_ut("Revati", jd, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH) 230 | to_dms_prec(galc[0]) 231 | [359, 49, 60.0] # == 359°50'00' 232 | ``` 233 | 234 | This date of 20 Jul 563 is tantalizingly close to Sassanian 20 Mar 564. 235 | 236 | Note that selecting `swe.SIDM_TRUE_REVATI` places Revati at exactly 360°(=0°) 237 | (instead of 359°50' like SS wants). It's zero-point is in 575 AD: 238 | 239 | ``` 240 | jd = swe.julday(575, 7, 18, 12 + 52/60. + 12.87/3600) 241 | swe.set_sid_mode(swe.SIDM_USER, jd, 0.0) 242 | galc = swe.fixstar_ut("Revati", jd, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH) 243 | to_dms_prec(galc[0]) 244 | [359, 59, 60.0] # == 360°00'00' = 0° 245 | ``` 246 | 247 | This Revati-at-zero is also mentioned in Siddhanta Siromani. 248 | 249 | ### True Citra 250 | 251 | A similar bisection search for Citra/Spica to be exactly 180° reveals: 252 | 253 | ``` 254 | julday = swe.julday(285, 1, 30, 11 + 55/60. + 10.92/3600) 255 | swe.set_sid_mode(swe.SIDM_USER, julday, 0.0) 256 | galc = swe.fixstar_ut("Citra", julday, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH) 257 | galc[0] 258 | 179.99999999999895 259 | ``` 260 | 261 | This date of 30 Jan 285 is very close to `swe.SIDM_TRUE_CITRA`'s 04 Oct 285 and 262 | `swe.SIDM_LAHIRI`'s 03 Sep 285. It is preferable to use SIDM_TRUE_CITRA because 263 | it seems to give better precision: 264 | 265 | ``` 266 | julday = 1825430.71351322 # TRUE_CITRA's zero day 267 | swe.set_sid_mode(swe.SIDM_TRUE_CITRA) 268 | galc = swe.fixstar_ut("Citra", julday, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH) 269 | galc[0] 270 | 179.99999999999994 271 | ``` 272 | 273 | ### True Pushya 274 | 275 | Surya Siddhanta mentions that Puṣya Nakṣatra (delta Cancri) is at 106°. This is 276 | also proposed by P V R Narasimha Rao. Once again, bisection search reveals the 277 | zero-point ayanamsa in 368 AD: 278 | 279 | ``` 280 | jd = swe.julday(368, 10, 28, 21 + 49/60. + 15.09/3600) # TRUE_PUSHYA's zero day 281 | swe.set_sid_mode(swe.SIDM_USER, jd, 0.0) 282 | galc = swe.fixstar_ut(",deCnc", jd, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH) 283 | to_dms_prec(galc[0]) 284 | [106, 0, 0.0] 285 | ``` 286 | 287 | ## Summary 288 | 289 | All these methods try to put zero point between 10 minutes and 20 minutes east 290 | of zeta Piscium: 291 | 292 | ``` 293 | t0 Position of zeta-Piscium 294 | on JD 2457288.0 295 | Hipparchan 09 Nov 545 [359, 38, 8.060755] 296 | Gal. Center@mid-Mūlā 09 Mar 550 [359, 41, 44.291252] 297 | SS Revati 05 Apr 556 [359, 46, 47.905798] 298 | Ushashashi 23 Jul 559 [359, 49, 32.962578] 299 | Revati@359°50' 20 Jul 563 [359, 52, 52.125081] 300 | Sassanian 20 Mar 564 [359, 53, 25.4508901] 301 | True Revati@0° 18 Jul 575 [0, 2, 51.516834] 302 | ``` 303 | 304 | SS Revati or Ushashashi seems to be a good middle-ground. Everybody seems to 305 | have become familiar with Ushashashi only due to Swiss Ephemeris. 306 | 307 | 308 | ### Miscellaneous Dates 309 | 310 | (All dates are proleptic Gregorian) 311 | 312 | Beginning of Kali Yuga = 23 Jan, -3102 (JD 588466) 313 | 314 | #### Krishna 315 | 316 | Lord Krishna's birthday is on śrāvaṇa-māsa-kṛṣṇa-pakṣa-aṣṭamī with Rohiṇī 317 | nakṣatra, born at midnight. 23(24) Jun, -3227 (JD 542596.54167). Location: 318 | Mathura (27.502°N, 77.683°E) -- this is using any Citra-pakṣa ayanāmśa 319 | (Lahiri/Raman/`SS_CITRA`/`TRUE_CITRA`) but also with Yukteshwar and 320 | GalCent_0SAG. Kali-Ahargana = -45869 days ~ 125.58 yrs. 321 | 322 | If you use a Revati-pakṣa (`SS_REVATI`/`TRUE_REVATI`/`USHASHASHI`/`SASSANIAN`) 323 | you get adhika-bhādrapada instead of śrāvaṇa. But it is still rohiṇī and 324 | kṛṣṇa-pakṣa-aṣṭamī. However, changing the date to 13 Jun, -3226 we get an exact 325 | match with these ayanāmśas as well. Kali-Ahargana = -45515 days ~ 124.61 yrs. 326 | 327 | If you use Krishnamurti ayanāmśa, you get Ārdrā instead of Rohiṇī. 328 | 329 | #### Buddha 330 | 331 | Lord Gautama Buddha's birthday is claimed as Vaishakha Masa Purnima, Tuesday, at 332 | about mid-day near Lumbini (27.484N, 83.276E). Modern historians place him in 333 | 563 BCE. Some (ex UNESCO) place him in 623 BCE. 334 | 335 | Using `SS_REVATI` or `TRUE_REVATI` gives the date as 23 Mar -561 which matches 336 | the above exactly. Taking -562 gives Thursday and -563 gives Sunday :( 337 | It's interesting that 20 Mar -623 is adhika-vaishaka-masa but Thursday. 18 Apr 338 | -623 is "normal" Vaishakha but Friday. 339 | -624 gives Saturday, 8 Apr -622 gives Wednesday. 28 Mar -621 gives Sunday. 340 | 341 | `SS_REVATI`: 7 Apr -622, Tuesday, Caturdasi ends after mid-day (12:21:06). 342 | `USHASHASHI`: 7 Apr -622, Tuesday, Caturdasi ends after mid-day (12:06:06). 343 | `TRUE_REVATI`: 7 Apr -622, Tuesday, Caturdasi ends after mid-day (12:21:06). 344 | 345 | #### Adi Shankara 346 | 347 | Traditional records claim he was born vaisakha-sukla-pancami, punarvasu, 348 | bhanuvara around mid-day. 349 | 350 | `SS_REVATI`: 19 Apr 788 CE has punarvasu after 11:45 AM and matches well, except 351 | that it is Tuesday instead of Sunday. The very next year (9 Apr 789 CE), 352 | vaisakha-sukla-pancami is indeed Sunday, but Ardra until Monday morning 4 AM. 353 | 354 | #### Caitanya Mahaprabhu 355 | 356 | His planetary positions are [known][cait]. He was born on Phalguna-purnima 357 | purvaphalguni nakshatra, around evening time in Simha lagna. According to 358 | `SS_REVATI`, for Simha lagna (~15:45 to 17:45) duration, all other planetary 359 | positions match, except that Rahu is in Meena instead of Kumbha. Same fate with 360 | Ushashashi, True Revati, etc. 361 | 362 | The popular ones match correctly all positions (Lahiri, Raman, KP, True 363 | Citra). Strangely enough, GALCENT_0SAG also matches! 364 | 365 | 366 | [cait]: http://www.dandavats.com/?p=8193 367 | 368 | #### Guru Nanak 369 | 370 | Traditional date: kartika-masa-purnima, krittika nakshatra, simha lagna, gurvara 371 | around midnight. Historians says 15 Apr 1469 Julian = 24 Apr 1469. 372 | 373 | Kartika masa is in October-November, so it can't be April, irrespective of 374 | Ayanamsa. 375 | 376 | 17-Nov-1470 matches all of the above. Indeed it is Simha lagna around 23:45. 377 | 378 | 29-Oct-1469 is Friday, Asvini nakshatra. Simha lagna around midnight on that day 379 | pushes it to 30-Oct-1969 before 4 AM which makes it Saturday, Bharani (both 380 | nakshatra and weekday mismatch for 1469) 381 | 382 | ##### Misc code 383 | ```python 384 | def galc_center(jd, mode): 385 | swe.set_sid_mode(mode) 386 | galc = swe.fixstar_ut("Gal. Center", jd, flag = swe.FLG_SIDEREAL | swe.FLG_SWIEPH | swe.FLG_NOABERR) 387 | return to_dms_prec(galc[0] % 30) 388 | ``` 389 | 390 | #### Julian to Gregorian date conversion in Swiss Eph 391 | 392 | Generally years before 1582 are quoted in (proleptic) Julian, not Gregorian. 393 | You can use `swe.date_conversion()` first and then apply `swe.revjul()`. 394 | 395 | For example: `21 Mar 499, 7:30:31.57 UT` actually means it is Julian date 396 | because 499 AD < 1582 AD. 397 | 398 | ```python 399 | swe.date_conversion(499, 3, 21, 7 + 30/60. + 31.57/3600., 'j') # [j]ulian 400 | (0, 1903396.8128653935) 401 | 402 | swe.revjul(_[1], swe.GREG_CAL) 403 | (499, 3, 22, 7.508769443258643) 404 | ``` 405 | 406 | So, 21 Mar became 22 Mar, just one day off. 407 | 408 | Similarly, Kali Yuga began on February 17/18, 3102 BC. Note that whenever BC is 409 | given, you've to substract 1 from it (irrespective of Gregorian/Julian). So 3102 410 | BC = -3101 411 | 412 | ```python 413 | swe.date_conversion(-3101, 2, 18, cal = 'j') 414 | (0, 588466.0) 415 | 416 | swe.revjul(_[1], swe.GREG_CAL) 417 | (-3101, 1, 23, 12.0) 418 | 419 | ahargana(588466) 420 | 0.5 421 | ``` 422 | 423 | Now 18 Feb 3102 BC (julian) became 23 Jan 3102 BC (greg)! 424 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # generated by wxGlade 0.6.4 on Tue Jan 22 15:06:05 2013 4 | 5 | # gui.py -- Displays a wxPython GUI for panchangam calculations 6 | # 7 | # Copyright (C) 2013 Satish BD 8 | # Downloaded from https://github.com/bdsatish/drik-panchanga 9 | # 10 | # This file is part of the "drik-panchanga" Python library 11 | # for computing Hindu luni-solar calendar based on the Swiss ephemeris 12 | # 13 | # This program is free software: you can redistribute it and/or modify 14 | # it under the terms of the GNU Affero General Public License as published by 15 | # the Free Software Foundation, either version 3 of the License, or 16 | # (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU Affero General Public License for more details. 22 | # 23 | # You should have received a copy of the GNU Affero General Public License 24 | # along with this program. If not, see . 25 | 26 | import wx 27 | import json 28 | import re 29 | 30 | from time import strptime 31 | from pytz import timezone, utc 32 | from datetime import datetime 33 | from panchanga import * 34 | import difflib 35 | 36 | # begin wxGlade: extracode 37 | # end wxGlade 38 | 39 | format_time = lambda t: "%02d:%02d:%02d" % (t[0], t[1], t[2]) 40 | 41 | class Panchanga(wx.Frame): 42 | def __init__(self, *args, **kwds): 43 | # begin wxGlade: Panchanga.__init__ 44 | kwds["style"] = wx.DEFAULT_FRAME_STYLE 45 | wx.Frame.__init__(self, *args, **kwds) 46 | self.dateTxt = wx.TextCtrl(self, wx.ID_ANY, "23/01/2013") 47 | self.searchBtn = wx.Button(self, wx.ID_ANY, "Search") 48 | self.placeTxt = wx.TextCtrl(self, wx.ID_ANY, "Bangalore") 49 | self.computeBtn = wx.Button(self, wx.ID_ANY, "Compute") 50 | self.latTxt = wx.TextCtrl(self, wx.ID_ANY, "12.97194", style=wx.TE_PROCESS_TAB) 51 | self.lonTxt = wx.TextCtrl(self, wx.ID_ANY, "77.59369", style=wx.TE_PROCESS_TAB) 52 | self.tzTxt = wx.TextCtrl(self, wx.ID_ANY, "+5.5") 53 | self.samvatTxt = wx.StaticText(self, wx.ID_ANY, "Nandana samvatsara") 54 | self.masaTxt = wx.StaticText(self, wx.ID_ANY, u"Pu\u1e63ya m\u0101sa") 55 | self.rituTxt = wx.StaticText(self, wx.ID_ANY, u"Hemanta \u1e5btu") 56 | self.tithiTxt = wx.StaticText(self, wx.ID_ANY, u"\u015aukla pak\u1e63a dv\u0101da\u1e63\u012b ") 57 | self.tithiTimeTxt = wx.StaticText(self, wx.ID_ANY, "28:43:28") 58 | self.nakTxt = wx.StaticText(self, wx.ID_ANY, u"Rohi\u1e47\u012b") 59 | self.nakTimeTxt = wx.StaticText(self, wx.ID_ANY, "06:57:56") 60 | self.yogaTxt = wx.StaticText(self, wx.ID_ANY, "Brahma") 61 | self.yogaTimeTxt = wx.StaticText(self, wx.ID_ANY, "08:32:02") 62 | self.karanaTxt = wx.StaticText(self, wx.ID_ANY, "Bhava") 63 | self.karanaTimeTxt = wx.StaticText(self, wx.ID_ANY, "15:27:47") 64 | self.varaTxt = wx.StaticText(self, wx.ID_ANY, u"Budhav\u0101ra ") 65 | self.aharTxt = wx.StaticText(self, wx.ID_ANY, "KaliDay 1867850") 66 | self.sakaTxt = wx.StaticText(self, wx.ID_ANY, u"\u015a\u0101liv\u0101hana \u015baka 1934 ") 67 | self.kaliTxt = wx.StaticText(self, wx.ID_ANY, "GataKali 5113") 68 | self.sunriseTxt = wx.StaticText(self, wx.ID_ANY, "06:47:38") 69 | self.sunsetTxt = wx.StaticText(self, wx.ID_ANY, "18:15:31") 70 | self.duraTxt = wx.StaticText(self, wx.ID_ANY, "11:27:52") 71 | self.sizer_1_staticbox = wx.StaticBox(self, wx.ID_ANY, "") 72 | 73 | self.__set_properties() 74 | self.__do_layout() 75 | 76 | self.Bind(wx.EVT_TEXT_ENTER, self.calculate_panchanga, self.dateTxt) 77 | self.Bind(wx.EVT_BUTTON, self.search_location, self.searchBtn) 78 | self.Bind(wx.EVT_TEXT_ENTER, self.search_location, self.placeTxt) 79 | self.Bind(wx.EVT_BUTTON, self.calculate_panchanga, self.computeBtn) 80 | self.Bind(wx.EVT_TEXT_ENTER, self.set_place, self.latTxt) 81 | self.Bind(wx.EVT_TEXT, self.set_place, self.latTxt) 82 | self.Bind(wx.EVT_TEXT_ENTER, self.set_place, self.lonTxt) 83 | self.Bind(wx.EVT_TEXT, self.set_place, self.lonTxt) 84 | self.Bind(wx.EVT_TEXT_ENTER, self.set_place, self.tzTxt) 85 | self.Bind(wx.EVT_TEXT, self.set_place, self.tzTxt) 86 | # end wxGlade 87 | 88 | now = datetime.now() 89 | self.dateTxt.SetValue("%d/%d/%d" % (now.day, now.month, now.year)) 90 | self.init_db() 91 | 92 | def __set_properties(self): 93 | # begin wxGlade: Panchanga.__set_properties 94 | self.SetTitle("Indian Calendar") 95 | self.SetSize((625, 575)) 96 | self.SetToolTip(wx.ToolTip("Can also be entered as: 77d 35' 37\"")) 97 | self.dateTxt.SetToolTip(wx.ToolTip("Enter date and then Location below. Negative years are treated as per proleptic Gregorian calendar.")) 98 | self.dateTxt.SetFocus() 99 | self.searchBtn.SetForegroundColour(wx.Colour(44, 44, 44)) 100 | self.placeTxt.SetToolTip(wx.ToolTip("Type location, click Search. If the search fails, enter Latitude, Longitude and Time zone directly below. Lastly, \"Compute\". Enter ASCII names only.")) 101 | self.latTxt.SetToolTip(wx.ToolTip("Can also be entered as: 12d 58' 19\"")) 102 | self.tzTxt.SetToolTip(wx.ToolTip("In hours. Positive values are east of UTC and negative, west of UTC.")) 103 | # end wxGlade 104 | 105 | def __do_layout(self): 106 | # begin wxGlade: Panchanga.__do_layout 107 | self.sizer_1_staticbox.Lower() 108 | sizer_1 = wx.StaticBoxSizer(self.sizer_1_staticbox, wx.HORIZONTAL) 109 | grid_sizer_1 = wx.GridSizer(17, 3, 0, 0) 110 | label_1 = wx.StaticText(self, wx.ID_ANY, "") 111 | grid_sizer_1.Add(label_1, 0, 0, 0) 112 | label_2 = wx.StaticText(self, wx.ID_ANY, u"D\u1e5bg-ga\u1e47ita Pa\u00f1c\u0101\u1e45ga", style=wx.ALIGN_CENTER | wx.ALIGN_RIGHT) 113 | label_2.SetMinSize((319, 24)) 114 | label_2.SetFont(wx.Font(15, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) 115 | grid_sizer_1.Add(label_2, 0, wx.ALIGN_CENTER_VERTICAL, 0) 116 | label_4 = wx.StaticText(self, wx.ID_ANY, "") 117 | grid_sizer_1.Add(label_4, 0, 0, 0) 118 | label_3 = wx.StaticText(self, wx.ID_ANY, "Date (DD/MM/YYYY)") 119 | grid_sizer_1.Add(label_3, 0, wx.ALIGN_CENTER, 0) 120 | grid_sizer_1.Add(self.dateTxt, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, 0) 121 | grid_sizer_1.Add(self.searchBtn, 0, 0, 0) 122 | label_6 = wx.StaticText(self, wx.ID_ANY, "Location") 123 | grid_sizer_1.Add(label_6, 0, wx.ALIGN_CENTER, 0) 124 | grid_sizer_1.Add(self.placeTxt, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 0) 125 | grid_sizer_1.Add(self.computeBtn, 0, 0, 0) 126 | label_31 = wx.StaticText(self, wx.ID_ANY, "Latitude") 127 | grid_sizer_1.Add(label_31, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 0) 128 | label_32 = wx.StaticText(self, wx.ID_ANY, "Longitude") 129 | grid_sizer_1.Add(label_32, 0, wx.ALIGN_CENTER, 0) 130 | label_33 = wx.StaticText(self, wx.ID_ANY, "Time zone") 131 | grid_sizer_1.Add(label_33, 0, wx.ALIGN_CENTER_VERTICAL, 0) 132 | grid_sizer_1.Add(self.latTxt, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.RIGHT, 0) 133 | grid_sizer_1.Add(self.lonTxt, 0, wx.ALIGN_CENTER | wx.ALIGN_RIGHT, 0) 134 | grid_sizer_1.Add(self.tzTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 135 | static_line_1 = wx.StaticLine(self, wx.ID_ANY) 136 | grid_sizer_1.Add(static_line_1, 0, wx.EXPAND, 0) 137 | static_line_2 = wx.StaticLine(self, wx.ID_ANY) 138 | grid_sizer_1.Add(static_line_2, 0, wx.EXPAND, 0) 139 | static_line_3 = wx.StaticLine(self, wx.ID_ANY) 140 | grid_sizer_1.Add(static_line_3, 0, wx.EXPAND, 0) 141 | grid_sizer_1.Add(self.samvatTxt, 0, wx.ALIGN_CENTER, 0) 142 | grid_sizer_1.Add(self.masaTxt, 0, wx.ALIGN_CENTER, 0) 143 | grid_sizer_1.Add(self.rituTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 144 | label_7 = wx.StaticText(self, wx.ID_ANY, "Tithi") 145 | grid_sizer_1.Add(label_7, 0, wx.ALIGN_CENTER, 0) 146 | grid_sizer_1.Add(self.tithiTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 147 | grid_sizer_1.Add(self.tithiTimeTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 148 | label_13 = wx.StaticText(self, wx.ID_ANY, u"Nak\u1e63atra ") 149 | grid_sizer_1.Add(label_13, 0, wx.ALIGN_CENTER, 0) 150 | grid_sizer_1.Add(self.nakTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 151 | grid_sizer_1.Add(self.nakTimeTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 152 | label_16 = wx.StaticText(self, wx.ID_ANY, "Yoga") 153 | grid_sizer_1.Add(label_16, 0, wx.ALIGN_CENTER, 0) 154 | grid_sizer_1.Add(self.yogaTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 155 | grid_sizer_1.Add(self.yogaTimeTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 156 | label_19 = wx.StaticText(self, wx.ID_ANY, u"Kara\u1e47a ") 157 | grid_sizer_1.Add(label_19, 0, wx.ALIGN_CENTER, 0) 158 | grid_sizer_1.Add(self.karanaTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 159 | grid_sizer_1.Add(self.karanaTimeTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 160 | label_22 = wx.StaticText(self, wx.ID_ANY, u"V\u0101ra ") 161 | grid_sizer_1.Add(label_22, 0, wx.ALIGN_CENTER, 0) 162 | grid_sizer_1.Add(self.varaTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 163 | label_24 = wx.StaticText(self, wx.ID_ANY, "") 164 | grid_sizer_1.Add(label_24, 0, 0, 0) 165 | grid_sizer_1.Add(self.aharTxt, 0, wx.ALIGN_CENTER, 0) 166 | grid_sizer_1.Add(self.sakaTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 167 | grid_sizer_1.Add(self.kaliTxt, 0, wx.ALIGN_CENTER_VERTICAL, 0) 168 | static_line_4 = wx.StaticLine(self, wx.ID_ANY) 169 | grid_sizer_1.Add(static_line_4, 0, wx.EXPAND, 0) 170 | static_line_5 = wx.StaticLine(self, wx.ID_ANY) 171 | grid_sizer_1.Add(static_line_5, 0, wx.EXPAND, 0) 172 | static_line_6 = wx.StaticLine(self, wx.ID_ANY) 173 | grid_sizer_1.Add(static_line_6, 0, wx.EXPAND, 0) 174 | label_34 = wx.StaticText(self, wx.ID_ANY, "Sunrise") 175 | grid_sizer_1.Add(label_34, 0, wx.ALIGN_CENTER, 0) 176 | label_37 = wx.StaticText(self, wx.ID_ANY, "Sunset") 177 | grid_sizer_1.Add(label_37, 0, wx.ALIGN_CENTER, 0) 178 | label_5 = wx.StaticText(self, wx.ID_ANY, "Day duration") 179 | grid_sizer_1.Add(label_5, 0, wx.ALIGN_CENTER, 0) 180 | grid_sizer_1.Add(self.sunriseTxt, 0, wx.ALIGN_CENTER, 0) 181 | grid_sizer_1.Add(self.sunsetTxt, 0, wx.ALIGN_CENTER, 0) 182 | grid_sizer_1.Add(self.duraTxt, 0, wx.ALIGN_CENTER, 0) 183 | label_10 = wx.StaticText(self, wx.ID_ANY, "") 184 | grid_sizer_1.Add(label_10, 0, 0, 0) 185 | label_9 = wx.StaticText(self, wx.ID_ANY, "https://github.com/bdsatish/drik-panchanga") 186 | grid_sizer_1.Add(label_9, 0, wx.ALIGN_CENTER, 0) 187 | sizer_1.Add(grid_sizer_1, 1, 0, 0) 188 | self.SetSizer(sizer_1) 189 | self.Layout() 190 | # end wxGlade 191 | 192 | def calculate_panchanga(self, event): # wxGlade: Panchanga. 193 | jd = gregorian_to_jd(self.parse_date()) 194 | self.set_place(event) 195 | place = self.place 196 | 197 | ti = tithi(jd, place) 198 | nak = nakshatra(jd, place) 199 | yog = yoga(jd, place) 200 | mas = masa(jd, place) 201 | rtu = ritu(mas[0]) 202 | 203 | kar = karana(jd, place) 204 | vara = vaara(jd) 205 | srise = sunrise(jd, place)[1] 206 | sset = sunset(jd, place)[1] 207 | kday = ahargana(jd) 208 | kyear, sakayr = elapsed_year(jd, mas[0]) 209 | samvat = samvatsara(jd, mas[0]) 210 | day_dur = day_duration(jd, place)[1] 211 | gauri = self.gauri_panchanga(jd) 212 | positions = self.kundali(jd) 213 | 214 | # Update GUI one by one. First the easy ones 215 | self.karanaTxt.SetLabel("%s" % self.karanas[str(kar[0])]) 216 | self.karanaTimeTxt.SetLabel(" -- ") 217 | self.varaTxt.SetLabel("%s" % self.vaaras[str(vara)]) 218 | self.sunriseTxt.SetLabel(format_time(srise)) 219 | self.sunsetTxt.SetLabel(format_time(sset)) 220 | self.sakaTxt.SetLabel(u"\u015a\u0101liv\u0101hana \u015baka %d" % (sakayr)) 221 | self.kaliTxt.SetLabel("GataKali %d" % (kyear)) 222 | self.aharTxt.SetLabel("KaliDay %d" % (kday)) 223 | self.rituTxt.SetLabel(u"%s \u1e5btu" % (self.ritus[str(rtu)])) 224 | self.samvatTxt.SetLabel("%s samvatsara" % (self.samvats[str(samvat)])) 225 | self.duraTxt.SetLabel(format_time(day_dur)) 226 | 227 | # Next update the complex ones 228 | month_name = self.masas[str(mas[0])] 229 | is_leap = mas[1] 230 | if is_leap: month_name = "Adhika " + month_name.lower() 231 | self.masaTxt.SetLabel(month_name + u" m\u0101sa") 232 | 233 | name, hms = format_name_hms(yog, self.yogas) 234 | self.yogaTxt.SetLabel(name) 235 | self.yogaTimeTxt.SetLabel(hms) 236 | 237 | name, hms = format_name_hms(ti, self.tithis) 238 | self.tithiTxt.SetLabel(name) 239 | self.tithiTimeTxt.SetLabel(hms) 240 | 241 | name, hms = format_name_hms(nak, self.nakshatras) 242 | self.nakTxt.SetLabel(name) 243 | self.nakTimeTxt.SetLabel(hms) 244 | 245 | event.Skip() 246 | 247 | 248 | def search_location(self, event): # wxGlade: Panchanga. 249 | city = self.placeTxt.Value.title() # Convert to title-case 250 | if self.cities.has_key(city): 251 | self.searchBtn.SetForegroundColour(wx.Colour(0x2C, 0x2C, 0x2C)) 252 | # self.searchBtn.SetLabel("Found!") 253 | 254 | date = self.parse_date() 255 | city = self.cities[city] 256 | lat = city['latitude'] 257 | lon = city['longitude'] 258 | tzname = city['timezone'] 259 | self.tzone = timezone(tzname) 260 | tz_offset = self.compute_timezone_offset() 261 | self.place = Place(lat, lon, tz_offset) 262 | 263 | # update coordinate textboxes 264 | self.latTxt.SetValue("%.5f" % lat) 265 | self.lonTxt.SetValue("%.5f" % lon) 266 | self.tzTxt.SetValue("%+.2f" % tz_offset) 267 | else: 268 | # Find nearest match 269 | nearest = difflib.get_close_matches(city, self.all_cities, 5) 270 | all_matches = "" 271 | for m in nearest: 272 | all_matches += m + '\n' 273 | msg = city + ' not found!\n\n' + 'Did you mean any of these?\n\n' + all_matches 274 | wx.MessageBox(msg, 'Error', wx.OK | wx.ICON_ERROR) 275 | 276 | event.Skip() 277 | 278 | def parse_date(self): 279 | date = self.dateTxt.Value 280 | try: 281 | dt = strptime(date, "%d/%m/%Y") 282 | date = Date(dt.tm_year, dt.tm_mon, dt.tm_mday) 283 | except ValueError: 284 | # Probably the user entered negative year, strptime can't handle it. 285 | day, month, year = map(int, date.split('/')) 286 | date = Date(year, month, day) 287 | return date 288 | 289 | def init_db(self): 290 | fp = open("cities.json") 291 | self.cities = json.load(fp) 292 | self.all_cities = self.cities.keys() 293 | fp.close() 294 | sktnames = load_json_file("sanskrit_names.json") 295 | self.tithis = sktnames["tithis"] 296 | self.nakshatras = sktnames["nakshatras"] 297 | self.vaaras = sktnames["varas"] 298 | self.yogas = sktnames["yogas"] 299 | self.karanas = sktnames["karanas"] 300 | self.masas = sktnames["masas"] 301 | self.samvats = sktnames["samvats"] 302 | self.ritus = sktnames["ritus"] 303 | self.gauri = sktnames["gauri"] 304 | self.planets = sktnames["planets"] 305 | self.zodiac = sktnames["zodiac"] 306 | 307 | def set_place(self, event): # wxGlade: Panchanga. 308 | lat = float(self.latTxt.Value) 309 | lon = float(self.lonTxt.Value) 310 | tz = float(self.tzTxt.Value) 311 | self.place = Place(lat, lon, tz) 312 | event.Skip() 313 | 314 | 315 | def compute_timezone_offset(self): 316 | date = self.parse_date() 317 | timezone = self.tzone 318 | dt = datetime(date.year, date.month, date.day) 319 | # offset from UTC (in hours). Needed especially for DST countries 320 | tz_offset = timezone.utcoffset(dt, is_dst = True).total_seconds() / 3600. 321 | return tz_offset 322 | 323 | def gauri_panchanga(self, jd): 324 | times = gauri_chogadiya(jd, self.place) 325 | vara = vaara(jd) 326 | names = self.gauri[str(vara)] 327 | 328 | return zip(names, times) 329 | 330 | def kundali(self, jd): 331 | place = self.place 332 | data = planetary_positions(jd, place) 333 | for position in data: 334 | graha = self.planets[str(position[0])] 335 | house = self.zodiac[str(position[1])] 336 | d, m, s = position[2] 337 | dms = u"%d°%d'%d\"" % (d, m, s) 338 | nak = self.nakshatras[str(position[3][0])] 339 | pada = position[3][1] 340 | 341 | # end of class Panchanga 342 | 343 | # Global functions 344 | # Load json file ignoring comments (// and /* ... */) 345 | def load_json_file(filename): 346 | comment = re.compile('(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?', 347 | re.DOTALL | re.MULTILINE) 348 | with open(filename) as fp: 349 | content = ''.join(fp.readlines()) 350 | match = comment.search(content) ## Look for comments 351 | while match: 352 | # single line comment 353 | content = content[:match.start()] + content[match.end():] 354 | match = comment.search(content) 355 | 356 | return json.loads(content) 357 | 358 | # Converts list [12, [23, 45, 50]] to lookup[12] and 23:45:50 359 | def format_name_hms(nhms, lookup): 360 | name_txt = lookup[str(nhms[0])] 361 | time_txt = format_time(nhms[1]) 362 | if len(nhms) == 4: 363 | name_txt += "\n" + lookup[str(nhms[2])] 364 | time_txt += "\n" + format_time(nhms[3]) 365 | 366 | return name_txt, time_txt 367 | 368 | 369 | if __name__ == "__main__": 370 | app = wx.App(False) 371 | frame_1 = Panchanga(None, -1, "") 372 | app.SetTopWindow(frame_1) 373 | frame_1.Show() 374 | app.MainLoop() 375 | -------------------------------------------------------------------------------- /Gui.wxg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Indian Calendar 8 | Can also be entered as: 77d 35' 37" 9 | 625, 575 10 | 11 | wxHORIZONTAL 12 | 13 | 14 | 0 15 | 16 | 17 | 0 18 | 17 19 | 3 20 | 0 21 | 22 | 0 23 | 24 | 25 | 0 26 | 27 | 28 | 29 | wxALIGN_CENTER_VERTICAL 30 | 0 31 | 32 | 33 | 34 | 0 35 | 36 | 37 | 15 38 | default 39 | 40 | bold 41 | 0 42 | 43 | 44 | 319, 24 45 | 46 | 47 | 48 | 0 49 | 50 | 51 | 0 52 | 53 | 54 | 55 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 56 | 0 57 | 58 | 59 | 0 60 | 61 | 62 | 63 | 64 | wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL 65 | 0 66 | 67 | 68 | Enter date and then Location below. Negative years are treated as per proleptic Gregorian calendar. 69 | 1 70 | 23/01/2013 71 | 72 | calculate_panchanga 73 | 74 | 75 | 76 | 77 | 0 78 | 79 | 80 | #2c2c2c 81 | 82 | 83 | search_location 84 | 85 | 86 | 87 | 88 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 89 | 0 90 | 91 | 92 | 0 93 | 94 | 95 | 96 | 97 | wxEXPAND|wxALIGN_CENTER_VERTICAL 98 | 0 99 | 100 | 101 | Type location, click Search. If the search fails, enter Latitude, Longitude and Time zone directly below. Lastly, "Compute". Enter ASCII names only. 102 | Bangalore 103 | 104 | search_location 105 | 106 | 107 | 108 | 109 | 0 110 | 111 | 112 | 113 | 114 | calculate_panchanga 115 | 116 | 117 | 118 | 119 | wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL 120 | 0 121 | 122 | 123 | 0 124 | 125 | 126 | 127 | 128 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 129 | 0 130 | 131 | 132 | 0 133 | 134 | 135 | 136 | 137 | wxALIGN_CENTER_VERTICAL 138 | 0 139 | 140 | 141 | 0 142 | 143 | 144 | 145 | 146 | wxRIGHT|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL 147 | 0 148 | 149 | 150 | 151 | Can also be entered as: 12d 58' 19" 152 | 12.97194 153 | 154 | set_place 155 | set_place 156 | 157 | 158 | 159 | 160 | wxALIGN_RIGHT|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 161 | 0 162 | 163 | 164 | 165 | 77.59369 166 | 167 | set_place 168 | set_place 169 | 170 | 171 | 172 | 173 | wxALIGN_CENTER_VERTICAL 174 | 0 175 | 176 | 177 | In hours. Positive values are east of UTC and negative, west of UTC. 178 | +5.5 179 | 180 | set_place 181 | set_place 182 | 183 | 184 | 185 | 186 | wxEXPAND 187 | 0 188 | 189 | 190 | 191 | 0 192 | 193 | 194 | 195 | wxEXPAND 196 | 0 197 | 198 | 199 | 200 | 0 201 | 202 | 203 | 204 | wxEXPAND 205 | 0 206 | 207 | 208 | 209 | 0 210 | 211 | 212 | 213 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 214 | 0 215 | 216 | 217 | 1 218 | 219 | 220 | 221 | 222 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 223 | 0 224 | 225 | 226 | 1 227 | 228 | 229 | 230 | 231 | wxALIGN_CENTER_VERTICAL 232 | 0 233 | 234 | 235 | 1 236 | 237 | 238 | 239 | 240 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 241 | 0 242 | 243 | 244 | 0 245 | 246 | 247 | 248 | 249 | wxALIGN_CENTER_VERTICAL 250 | 0 251 | 252 | 253 | 1 254 | 255 | 256 | 257 | 258 | wxALIGN_CENTER_VERTICAL 259 | 0 260 | 261 | 262 | 1 263 | 264 | 265 | 266 | 267 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 268 | 0 269 | 270 | 271 | 0 272 | 273 | 274 | 275 | 276 | wxALIGN_CENTER_VERTICAL 277 | 0 278 | 279 | 280 | 1 281 | 282 | 283 | 284 | 285 | wxALIGN_CENTER_VERTICAL 286 | 0 287 | 288 | 289 | 1 290 | 291 | 292 | 293 | 294 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 295 | 0 296 | 297 | 298 | 0 299 | 300 | 301 | 302 | 303 | wxALIGN_CENTER_VERTICAL 304 | 0 305 | 306 | 307 | 1 308 | 309 | 310 | 311 | 312 | wxALIGN_CENTER_VERTICAL 313 | 0 314 | 315 | 316 | 1 317 | 318 | 319 | 320 | 321 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 322 | 0 323 | 324 | 325 | 0 326 | 327 | 328 | 329 | 330 | wxALIGN_CENTER_VERTICAL 331 | 0 332 | 333 | 334 | 1 335 | 336 | 337 | 338 | 339 | wxALIGN_CENTER_VERTICAL 340 | 0 341 | 342 | 343 | 1 344 | 345 | 346 | 347 | 348 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 349 | 0 350 | 351 | 352 | 0 353 | 354 | 355 | 356 | 357 | wxALIGN_CENTER_VERTICAL 358 | 0 359 | 360 | 361 | 1 362 | 363 | 364 | 365 | 366 | 0 367 | 368 | 369 | 0 370 | 371 | 372 | 373 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 374 | 0 375 | 376 | 377 | 1 378 | 379 | 380 | 381 | 382 | wxALIGN_CENTER_VERTICAL 383 | 0 384 | 385 | 386 | 1 387 | 388 | 389 | 390 | 391 | wxALIGN_CENTER_VERTICAL 392 | 0 393 | 394 | 395 | 1 396 | 397 | 398 | 399 | 400 | wxEXPAND 401 | 0 402 | 403 | 404 | 405 | 0 406 | 407 | 408 | 409 | wxEXPAND 410 | 0 411 | 412 | 413 | 414 | 0 415 | 416 | 417 | 418 | wxEXPAND 419 | 0 420 | 421 | 422 | 423 | 0 424 | 425 | 426 | 427 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 428 | 0 429 | 430 | 431 | 0 432 | 433 | 434 | 435 | 436 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 437 | 0 438 | 439 | 440 | 0 441 | 442 | 443 | 444 | 445 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 446 | 0 447 | 448 | 449 | 0 450 | 451 | 452 | 453 | 454 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 455 | 0 456 | 457 | 458 | 1 459 | 460 | 461 | 462 | 463 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 464 | 0 465 | 466 | 467 | 1 468 | 469 | 470 | 471 | 472 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 473 | 0 474 | 475 | 476 | 1 477 | 478 | 479 | 480 | 481 | 0 482 | 483 | 484 | 0 485 | 486 | 487 | 488 | wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 489 | 0 490 | 491 | 492 | 0 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | -------------------------------------------------------------------------------- /panchanga.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | # panchanga.py -- routines for computing tithi, vara, etc. 5 | # 6 | # Copyright (C) 2013 Satish BD 7 | # Downloaded from https://github.com/bdsatish/drik-panchanga 8 | # 9 | # This file is part of the "drik-panchanga" Python library 10 | # for computing Hindu luni-solar calendar based on the Swiss ephemeris 11 | # 12 | # This program is free software: you can redistribute it and/or modify 13 | # it under the terms of the GNU Affero General Public License as published by 14 | # the Free Software Foundation, either version 3 of the License, or 15 | # (at your option) any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU Affero General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Affero General Public License 23 | # along with this program. If not, see . 24 | 25 | """ 26 | Use Swiss ephemeris to calculate tithi, nakshatra, etc. 27 | """ 28 | 29 | from __future__ import division 30 | from math import ceil 31 | from collections import namedtuple as struct 32 | import swisseph as swe 33 | 34 | Date = struct('Date', ['year', 'month', 'day']) 35 | Place = struct('Place', ['latitude', 'longitude', 'timezone']) 36 | 37 | sidereal_year = 365.256360417 # From WolframAlpha 38 | 39 | # Hindu sunrise/sunset is calculated w.r.t middle of the sun's disk 40 | # They are geomretic, i.e. "true sunrise/set", so refraction is not considered 41 | _rise_flags = swe.BIT_DISC_CENTER + swe.BIT_NO_REFRACTION 42 | 43 | # namah suryaya chandraya mangalaya ... rahuve ketuve namah 44 | swe.KETU = swe.PLUTO # I've mapped Pluto to Ketu 45 | planet_list = [swe.SUN, swe.MOON, swe.MARS, swe.MERCURY, swe.JUPITER, 46 | swe.VENUS, swe.SATURN, swe.MEAN_NODE, # Rahu = MEAN_NODE 47 | swe.KETU, swe.URANUS, swe.NEPTUNE ] 48 | 49 | revati_359_50 = lambda: swe.set_sid_mode(swe.SIDM_USER, 1926892.343164331, 0) 50 | galc_cent_mid_mula = lambda: swe.set_sid_mode(swe.SIDM_USER, 1922011.128853056, 0) 51 | 52 | set_ayanamsa_mode = lambda: swe.set_sid_mode(swe.SIDM_LAHIRI) 53 | reset_ayanamsa_mode = lambda: swe.set_sid_mode(swe.SIDM_FAGAN_BRADLEY) 54 | 55 | # Temporary function 56 | def get_planet_name(planet): 57 | names = { swe.SURYA: 'Surya', swe.CHANDRA: 'Candra', swe.KUJA: 'Mangala', 58 | swe.BUDHA: 'Budha', swe.GURU: 'Guru', swe.SUKRA: 'Sukra', 59 | swe.SANI: 'Sani', swe.RAHU: 'Rahu', swe.KETU: 'Ketu', swe.PLUTO: 'Ketu'} 60 | return names[planet] 61 | 62 | # Convert 23d 30' 30" to 23.508333 degrees 63 | from_dms = lambda degs, mins, secs: degs + mins/60 + secs/3600 64 | 65 | # the inverse 66 | def to_dms_prec(deg): 67 | d = int(deg) 68 | mins = (deg - d) * 60 69 | m = int(mins) 70 | s = round((mins - m) * 60, 6) 71 | return [d, m, s] 72 | 73 | def to_dms(deg): 74 | d, m, s = to_dms_prec(deg) 75 | return [d, m, int(s)] 76 | 77 | def unwrap_angles(angles): 78 | """Add 360 to those elements in the input list so that 79 | all elements are sorted in ascending order.""" 80 | result = angles 81 | for i in range(1, len(angles)): 82 | if result[i] < result[i-1]: result[i] += 360 83 | 84 | assert(result == sorted(result)) 85 | return result 86 | 87 | # Make angle lie between [-180, 180) instead of [0, 360) 88 | norm180 = lambda angle: (angle - 360) if angle >= 180 else angle; 89 | 90 | # Make angle lie between [0, 360) 91 | norm360 = lambda angle: angle % 360 92 | 93 | # Ketu is always 180° after Rahu, so same coordinates but different constellations 94 | # i.e if Rahu is in Pisces, Ketu is in Virgo etc 95 | ketu = lambda rahu: (rahu + 180) % 360 96 | 97 | def function(point): 98 | swe.set_sid_mode(swe.SIDM_USER, point, 0.0) 99 | #swe.set_sid_mode(swe.SIDM_LAHIRI) 100 | # Place Revati at 359°50' 101 | #fval = norm180(swe.fixstar_ut("Revati", point, flag = swe.FLG_SWIEPH | swe.FLG_SIDEREAL)[0]) - ((359 + 49/60 + 59/3600) - 360) 102 | # Place Revati at 0°0'0" 103 | #fval = norm180(swe.fixstar_ut("Revati", point, flag = swe.FLG_SWIEPH | swe.FLG_SIDEREAL)[0]) 104 | # Place Citra at 180° 105 | fval = swe.fixstar_ut("Citra", point, flag = swe.FLG_SWIEPH | swe.FLG_SIDEREAL)[0] - (180) 106 | # Place Pushya (delta Cancri) at 106° 107 | # fval = swe.fixstar_ut(",deCnc", point, flag = swe.FLG_SWIEPH | swe.FLG_SIDEREAL)[0] - (106) 108 | return fval 109 | 110 | def bisection_search(func, start, stop): 111 | left = start 112 | right = stop 113 | epsilon = 5E-10 # Anything better than this puts the loop below infinite 114 | 115 | while True: 116 | middle = (left + right) / 2 117 | midval = func(middle) 118 | rtval = func(right) 119 | if midval * rtval >= 0: 120 | right = middle 121 | else: 122 | left = middle 123 | 124 | if (right - left) <= epsilon: break 125 | 126 | return (right + left) / 2 127 | 128 | def inverse_lagrange(x, y, ya): 129 | """Given two lists x and y, find the value of x = xa when y = ya, i.e., f(xa) = ya""" 130 | assert(len(x) == len(y)) 131 | total = 0 132 | for i in range(len(x)): 133 | numer = 1 134 | denom = 1 135 | for j in range(len(x)): 136 | if j != i: 137 | numer *= (ya - y[j]) 138 | denom *= (y[i] - y[j]) 139 | 140 | total += numer * x[i] / denom 141 | 142 | return total 143 | 144 | # Julian Day number as on (year, month, day) at 00:00 UTC 145 | gregorian_to_jd = lambda date: swe.julday(date.year, date.month, date.day, 0.0) 146 | jd_to_gregorian = lambda jd: swe.revjul(jd, swe.GREG_CAL) # returns (y, m, d, h, min, s) 147 | 148 | def local_time_to_jdut1(year, month, day, hour = 0, minutes = 0, seconds = 0, timezone = 0.0): 149 | """Converts local time to JD(UT1)""" 150 | y, m, d, h, mnt, s = swe.utc_time_zone(year, month, day, hour, minutes, seconds, timezone) 151 | # BUG in pyswisseph: replace 0 by s 152 | jd_et, jd_ut1 = swe.utc_to_jd(y, m, d, h, mnt, 0, flag = swe.GREG_CAL) 153 | return jd_ut1 154 | 155 | def nakshatra_pada(longitude): 156 | """Gives nakshatra (1..27) and paada (1..4) in which given longitude lies""" 157 | # 27 nakshatras span 360° 158 | one_star = (360 / 27) # = 13°20' 159 | # Each nakshatra has 4 padas, so 27 x 4 = 108 padas in 360° 160 | one_pada = (360 / 108) # = 3°20' 161 | quotient = int(longitude / one_star) 162 | reminder = (longitude - quotient * one_star) 163 | pada = int(reminder / one_pada) 164 | # convert 0..26 to 1..27 and 0..3 to 1..4 165 | return [1 + quotient, 1 + pada] 166 | 167 | def sidereal_longitude(jd, planet): 168 | """Computes nirayana (sidereal) longitude of given planet on jd""" 169 | set_ayanamsa_mode() 170 | longi = swe.calc_ut(jd, planet, flag = swe.FLG_SWIEPH | swe.FLG_SIDEREAL) 171 | reset_ayanamsa_mode() 172 | return norm360(longi[0]) # degrees 173 | 174 | solar_longitude = lambda jd: sidereal_longitude(jd, swe.SUN) 175 | lunar_longitude = lambda jd: sidereal_longitude(jd, swe.MOON) 176 | 177 | def sunrise(jd, place): 178 | """Sunrise when centre of disc is at horizon for given date and place""" 179 | lat, lon, tz = place 180 | result = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE) 181 | rise = result[1][0] # julian-day number 182 | # Convert to local time 183 | return [rise + tz/24., to_dms((rise - jd) * 24 + tz)] 184 | 185 | def sunset(jd, place): 186 | """Sunset when centre of disc is at horizon for given date and place""" 187 | lat, lon, tz = place 188 | result = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_SET) 189 | setting = result[1][0] # julian-day number 190 | # Convert to local time 191 | return [setting + tz/24., to_dms((setting - jd) * 24 + tz)] 192 | 193 | def moonrise(jd, place): 194 | """Moonrise when centre of disc is at horizon for given date and place""" 195 | lat, lon, tz = place 196 | result = swe.rise_trans(jd - tz/24, swe.MOON, lon, lat, rsmi = _rise_flags + swe.CALC_RISE) 197 | rise = result[1][0] # julian-day number 198 | # Convert to local time 199 | return to_dms((rise - jd) * 24 + tz) 200 | 201 | def moonset(jd, place): 202 | """Moonset when centre of disc is at horizon for given date and place""" 203 | lat, lon, tz = place 204 | result = swe.rise_trans(jd - tz/24, swe.MOON, lon, lat, rsmi = _rise_flags + swe.CALC_SET) 205 | setting = result[1][0] # julian-day number 206 | # Convert to local time 207 | return to_dms((setting - jd) * 24 + tz) 208 | 209 | # Tithi doesn't depend on Ayanamsa 210 | def tithi(jd, place): 211 | """Tithi at sunrise for given date and place. Also returns tithi's end time.""" 212 | tz = place.timezone 213 | # 1. Find time of sunrise 214 | rise = sunrise(jd, place)[0] - tz / 24 215 | 216 | # 2. Find tithi at this JDN 217 | moon_phase = lunar_phase(rise) 218 | today = ceil(moon_phase / 12) 219 | degrees_left = today * 12 - moon_phase 220 | 221 | # 3. Compute longitudinal differences at intervals of 0.25 days from sunrise 222 | offsets = [0.25, 0.5, 0.75, 1.0] 223 | lunar_long_diff = [ (lunar_longitude(rise + t) - lunar_longitude(rise)) % 360 for t in offsets ] 224 | solar_long_diff = [ (solar_longitude(rise + t) - solar_longitude(rise)) % 360 for t in offsets ] 225 | relative_motion = [ moon - sun for (moon, sun) in zip(lunar_long_diff, solar_long_diff) ] 226 | 227 | # 4. Find end time by 4-point inverse Lagrange interpolation 228 | y = relative_motion 229 | x = offsets 230 | # compute fraction of day (after sunrise) needed to traverse 'degrees_left' 231 | approx_end = inverse_lagrange(x, y, degrees_left) 232 | ends = (rise + approx_end -jd) * 24 + tz 233 | answer = [int(today), to_dms(ends)] 234 | 235 | # 5. Check for skipped tithi 236 | moon_phase_tmrw = lunar_phase(rise + 1) 237 | tomorrow = ceil(moon_phase_tmrw / 12) 238 | isSkipped = (tomorrow - today) % 30 > 1 239 | if isSkipped: 240 | # interpolate again with same (x,y) 241 | leap_tithi = today + 1 242 | degrees_left = leap_tithi * 12 - moon_phase 243 | approx_end = inverse_lagrange(x, y, degrees_left) 244 | ends = (rise + approx_end -jd) * 24 + place.timezone 245 | leap_tithi = 1 if today == 30 else leap_tithi 246 | answer += [int(leap_tithi), to_dms(ends)] 247 | 248 | return answer 249 | 250 | 251 | def nakshatra(jd, place): 252 | """Current nakshatra as of julian day (jd) 253 | 1 = Asvini, 2 = Bharani, ..., 27 = Revati 254 | """ 255 | # 1. Find time of sunrise 256 | lat, lon, tz = place 257 | rise = sunrise(jd, place)[0] - tz / 24. # Sunrise at UT 00:00 258 | 259 | offsets = [0.0, 0.25, 0.5, 0.75, 1.0] 260 | longitudes = [ lunar_longitude(rise + t) for t in offsets] 261 | 262 | # 2. Today's nakshatra is when offset = 0 263 | # There are 27 Nakshatras spanning 360 degrees 264 | nak = ceil(longitudes[0] * 27 / 360) 265 | 266 | # 3. Find end time by 5-point inverse Lagrange interpolation 267 | y = unwrap_angles(longitudes) 268 | x = offsets 269 | approx_end = inverse_lagrange(x, y, nak * 360 / 27) 270 | ends = (rise - jd + approx_end) * 24 + tz 271 | answer = [int(nak), to_dms(ends)] 272 | 273 | # 4. Check for skipped nakshatra 274 | nak_tmrw = ceil(longitudes[-1] * 27 / 360) 275 | isSkipped = (nak_tmrw - nak) % 27 > 1 276 | if isSkipped: 277 | leap_nak = nak + 1 278 | approx_end = inverse_lagrange(offsets, longitudes, leap_nak * 360 / 27) 279 | ends = (rise - jd + approx_end) * 24 + tz 280 | leap_nak = 1 if nak == 27 else leap_nak 281 | answer += [int(leap_nak), to_dms(ends)] 282 | 283 | return answer 284 | 285 | 286 | def yoga(jd, place): 287 | """Yoga at given jd and place. 288 | 1 = Vishkambha, 2 = Priti, ..., 27 = Vaidhrti 289 | """ 290 | # 1. Find time of sunrise 291 | lat, lon, tz = place 292 | rise = sunrise(jd, place)[0] - tz / 24. # Sunrise at UT 00:00 293 | 294 | # 2. Find the Nirayana longitudes and add them 295 | lunar_long = lunar_longitude(rise) 296 | solar_long = solar_longitude(rise) 297 | total = (lunar_long + solar_long) % 360 298 | # There are 27 Yogas spanning 360 degrees 299 | yog = ceil(total * 27 / 360) 300 | 301 | # 3. Find how many longitudes is there left to be swept 302 | degrees_left = yog * (360 / 27) - total 303 | 304 | # 3. Compute longitudinal sums at intervals of 0.25 days from sunrise 305 | offsets = [0.25, 0.5, 0.75, 1.0] 306 | lunar_long_diff = [ (lunar_longitude(rise + t) - lunar_longitude(rise)) % 360 for t in offsets ] 307 | solar_long_diff = [ (solar_longitude(rise + t) - solar_longitude(rise)) % 360 for t in offsets ] 308 | total_motion = [ moon + sun for (moon, sun) in zip(lunar_long_diff, solar_long_diff) ] 309 | 310 | # 4. Find end time by 4-point inverse Lagrange interpolation 311 | y = total_motion 312 | x = offsets 313 | # compute fraction of day (after sunrise) needed to traverse 'degrees_left' 314 | approx_end = inverse_lagrange(x, y, degrees_left) 315 | ends = (rise + approx_end - jd) * 24 + tz 316 | answer = [int(yog), to_dms(ends)] 317 | 318 | # 5. Check for skipped yoga 319 | lunar_long_tmrw = lunar_longitude(rise + 1) 320 | solar_long_tmrw = solar_longitude(rise + 1) 321 | total_tmrw = (lunar_long_tmrw + solar_long_tmrw) % 360 322 | tomorrow = ceil(total_tmrw * 27 / 360) 323 | isSkipped = (tomorrow - yog) % 27 > 1 324 | if isSkipped: 325 | # interpolate again with same (x,y) 326 | leap_yog = yog + 1 327 | degrees_left = leap_yog * (360 / 27) - total 328 | approx_end = inverse_lagrange(x, y, degrees_left) 329 | ends = (rise + approx_end - jd) * 24 + tz 330 | leap_yog = 1 if yog == 27 else leap_yog 331 | answer += [int(leap_yog), to_dms(ends)] 332 | 333 | return answer 334 | 335 | 336 | def karana(jd, place): 337 | """Returns the karana and their ending times. (from 1 to 60)""" 338 | # 1. Find time of sunrise 339 | rise = sunrise(jd, place)[0] 340 | 341 | # 2. Find karana at this JDN 342 | solar_long = solar_longitude(rise) 343 | lunar_long = lunar_longitude(rise) 344 | moon_phase = (lunar_long - solar_long) % 360 345 | today = ceil(moon_phase / 6) 346 | degrees_left = today * 6 - moon_phase 347 | 348 | return [int(today)] 349 | 350 | def vaara(jd): 351 | """Weekday for given Julian day. 0 = Sunday, 1 = Monday,..., 6 = Saturday""" 352 | return int(ceil(jd + 1) % 7) 353 | 354 | def masa(jd, place): 355 | """Returns lunar month and if it is adhika or not. 356 | 1 = Chaitra, 2 = Vaisakha, ..., 12 = Phalguna""" 357 | ti = tithi(jd, place)[0] 358 | critical = sunrise(jd, place)[0] # - tz/24 ? 359 | last_new_moon = new_moon(critical, ti, -1) 360 | next_new_moon = new_moon(critical, ti, +1) 361 | this_solar_month = raasi(last_new_moon) 362 | next_solar_month = raasi(next_new_moon) 363 | is_leap_month = (this_solar_month == next_solar_month) 364 | maasa = this_solar_month + 1 365 | if maasa > 12: maasa = (maasa % 12) 366 | return [int(maasa), is_leap_month] 367 | 368 | # epoch-midnight to given midnight 369 | # Days elapsed since beginning of Kali Yuga 370 | ahargana = lambda jd: jd - 588465.5 371 | 372 | def elapsed_year(jd, maasa_num): 373 | ahar = ahargana(jd) # or (jd + sunrise(jd, place)[0]) 374 | kali = int((ahar + (4 - maasa_num) * 30) / sidereal_year) 375 | saka = kali - 3179 376 | vikrama = saka + 135 377 | return kali, saka 378 | 379 | # New moon day: sun and moon have same longitude (0 degrees = 360 degrees difference) 380 | # Full moon day: sun and moon are 180 deg apart 381 | def new_moon(jd, tithi_, opt = -1): 382 | """Returns JDN, where 383 | opt = -1: JDN < jd such that lunar_phase(JDN) = 360 degrees 384 | opt = +1: JDN >= jd such that lunar_phase(JDN) = 360 degrees 385 | """ 386 | if opt == -1: start = jd - tithi_ # previous new moon 387 | if opt == +1: start = jd + (30 - tithi_) # next new moon 388 | # Search within a span of (start +- 2) days 389 | x = [ -2 + offset/4 for offset in range(17) ] 390 | y = [lunar_phase(start + i) for i in x] 391 | y = unwrap_angles(y) 392 | y0 = inverse_lagrange(x, y, 360) 393 | return start + y0 394 | 395 | def raasi(jd): 396 | """Zodiac of given jd. 1 = Mesha, ... 12 = Meena""" 397 | s = solar_longitude(jd) 398 | solar_nirayana = solar_longitude(jd) 399 | # 12 rasis occupy 360 degrees, so each one is 30 degrees 400 | return ceil(solar_nirayana / 30.) 401 | 402 | def lunar_phase(jd): 403 | solar_long = solar_longitude(jd) 404 | lunar_long = lunar_longitude(jd) 405 | moon_phase = (lunar_long - solar_long) % 360 406 | return moon_phase 407 | 408 | def samvatsara(jd, maasa_num): 409 | kali = elapsed_year(jd, maasa_num)[0] 410 | # Change 14 to 0 for North Indian tradition 411 | # See the function "get_Jovian_Year_name_south" in pancanga.pl 412 | if kali >= 4009: kali = (kali - 14) % 60 413 | samvat = (kali + 27 + int((kali * 211 - 108) / 18000)) % 60 414 | return samvat 415 | 416 | def ritu(masa_num): 417 | """0 = Vasanta,...,5 = Shishira""" 418 | return (masa_num - 1) // 2 419 | 420 | def day_duration(jd, place): 421 | srise = sunrise(jd, place)[0] # julian day num 422 | sset = sunset(jd, place)[0] # julian day num 423 | diff = (sset - srise) * 24 # In hours 424 | return [diff, to_dms(diff)] 425 | 426 | # The day duration is divided into 8 parts 427 | # Similarly night duration 428 | def gauri_chogadiya(jd, place): 429 | lat, lon, tz = place 430 | tz = place.timezone 431 | srise = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 432 | sset = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_SET)[1][0] 433 | day_dur = (sset - srise) 434 | 435 | end_times = [] 436 | for i in range(1, 9): 437 | end_times.append(to_dms((srise + (i * day_dur) / 8 - jd) * 24 + tz)) 438 | 439 | # Night duration = time from today's sunset to tomorrow's sunrise 440 | srise = swe.rise_trans((jd + 1) - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 441 | night_dur = (srise - sset) 442 | for i in range(1, 9): 443 | end_times.append(to_dms((sset + (i * night_dur) / 8 - jd) * 24 + tz)) 444 | 445 | return end_times 446 | 447 | def trikalam(jd, place, option='rahu'): 448 | lat, lon, tz = place 449 | tz = place.timezone 450 | srise = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 451 | sset = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_SET)[1][0] 452 | day_dur = (sset - srise) 453 | weekday = vaara(jd) 454 | 455 | # value in each array is for given weekday (0 = sunday, etc.) 456 | offsets = { 'rahu': [0.875, 0.125, 0.75, 0.5, 0.625, 0.375, 0.25], 457 | 'gulika': [0.75, 0.625, 0.5, 0.375, 0.25, 0.125, 0.0], 458 | 'yamaganda': [0.5, 0.375, 0.25, 0.125, 0.0, 0.75, 0.625] } 459 | 460 | start_time = srise + day_dur * offsets[option][weekday] 461 | end_time = start_time + 0.125 * day_dur 462 | 463 | # to local timezone 464 | start_time = (start_time - jd) * 24 + tz 465 | end_time = (end_time - jd) * 24 + tz 466 | return [to_dms(start_time), to_dms(end_time)] # decimal hours to H:M:S 467 | 468 | rahu_kalam = lambda jd, place: trikalam(jd, place, 'rahu') 469 | yamaganda_kalam = lambda jd, place: trikalam(jd, place, 'yamaganda') 470 | gulika_kalam = lambda jd, place: trikalam(jd, place, 'gulika') 471 | 472 | def durmuhurtam(jd, place): 473 | lat, lon, tz = place 474 | tz = place.timezone 475 | 476 | # Night = today's sunset to tomorrow's sunrise 477 | sset = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_SET)[1][0] 478 | srise = swe.rise_trans((jd + 1) - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 479 | night_dur = (srise - sset) 480 | 481 | # Day = today's sunrise to today's sunset 482 | srise = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 483 | day_dur = (sset - srise) 484 | 485 | weekday = vaara(jd) 486 | 487 | # There is one durmuhurtam on Sun, Wed, Sat; the rest have two 488 | offsets = [[10.4, 0.0], # Sunday 489 | [6.4, 8.8], # Monday 490 | [2.4, 4.8], # Tuesday, [day_duration , night_duration] 491 | [5.6, 0.0], # Wednesday 492 | [4.0, 8.8], # Thursday 493 | [2.4, 6.4], # Friday 494 | [1.6, 0.0]] # Saturday 495 | 496 | # second durmuhurtam of tuesday uses night_duration instead of day_duration 497 | dur = [day_dur, day_dur] 498 | base = [srise, srise] 499 | if weekday == 2: dur[1] = night_dur; base[1] = sset 500 | 501 | # compute start and end timings 502 | start_times = [0, 0] 503 | end_times = [0, 0] 504 | for i in range(0, 2): 505 | offset = offsets[weekday][i] 506 | if offset != 0.0: 507 | start_times[i] = base[i] + dur[i] * offsets[weekday][i] / 12 508 | end_times[i] = start_times[i] + day_dur * 0.8 / 12 509 | 510 | # convert to local time 511 | start_times[i] = (start_times[i] - jd) * 24 + tz 512 | end_times[i] = (end_times[i] - jd) * 24 + tz 513 | 514 | return [start_times, end_times] # in decimal hours 515 | 516 | def abhijit_muhurta(jd, place): 517 | """Abhijit muhurta is the 8th muhurta (middle one) of the 15 muhurtas 518 | during the day_duration (~12 hours)""" 519 | lat, lon, tz = place 520 | tz = place.timezone 521 | srise = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_RISE)[1][0] 522 | sset = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi = _rise_flags + swe.CALC_SET)[1][0] 523 | day_dur = (sset - srise) 524 | 525 | start_time = srise + 7 / 15 * day_dur 526 | end_time = srise + 8 / 15 * day_dur 527 | 528 | # to local time 529 | return [(start_time - jd) * 24 + tz, (end_time - jd) * 24 + tz] 530 | 531 | # 'jd' can be any time: ex, 2015-09-19 14:20 UTC 532 | # today = swe.julday(2015, 9, 19, 14 + 20./60) 533 | def planetary_positions(jd, place): 534 | """Computes instantaneous planetary positions 535 | (i.e., which celestial object lies in which constellation) 536 | 537 | Also gives the nakshatra-pada division 538 | """ 539 | jd_ut = jd - place.timezone / 24. 540 | 541 | positions = [] 542 | for planet in planet_list: 543 | if planet != swe.KETU: 544 | nirayana_long = sidereal_longitude(jd_ut, planet) 545 | else: # Ketu 546 | nirayana_long = ketu(sidereal_longitude(jd_ut, swe.RAHU)) 547 | 548 | # 12 zodiac signs span 360°, so each one takes 30° 549 | # 0 = Mesha, 1 = Vrishabha, ..., 11 = Meena 550 | constellation = int(nirayana_long / 30) 551 | coordinates = to_dms(nirayana_long % 30) 552 | positions.append([planet, constellation, coordinates, nakshatra_pada(nirayana_long)]) 553 | 554 | return positions 555 | 556 | def ascendant(jd, place): 557 | """Lagna (=ascendant) calculation at any given time & place""" 558 | lat, lon, tz = place 559 | jd_utc = jd - (tz / 24.) 560 | set_ayanamsa_mode() # needed for swe.houses_ex() 561 | 562 | # returns two arrays, cusps and ascmc, where ascmc[0] = Ascendant 563 | nirayana_lagna = swe.houses_ex(jd_utc, lat, lon, flag = swe.FLG_SIDEREAL)[1][0] 564 | # 12 zodiac signs span 360°, so each one takes 30° 565 | # 0 = Mesha, 1 = Vrishabha, ..., 11 = Meena 566 | constellation = int(nirayana_lagna / 30) 567 | coordinates = to_dms(nirayana_lagna % 30) 568 | 569 | reset_ayanamsa_mode() 570 | return [constellation, coordinates, nakshatra_pada(nirayana_lagna)] 571 | 572 | # http://www.oocities.org/talk2astrologer/LearnAstrology/Details/Navamsa.html 573 | # Useful for making D9 divisional chart 574 | def navamsa_from_long(longitude): 575 | """Calculates the navamsa-sign in which given longitude falls 576 | 0 = Aries, 1 = Taurus, ..., 11 = Pisces 577 | """ 578 | one_pada = (360 / (12 * 9)) # There are also 108 navamsas 579 | one_sign = 12 * one_pada # = 40 degrees exactly 580 | signs_elapsed = longitude / one_sign 581 | fraction_left = signs_elapsed % 1 582 | return int(fraction_left * 12) 583 | 584 | def navamsa(jd, place): 585 | """Calculates navamsa of all planets""" 586 | jd_utc = jd - place.timezone / 24. 587 | 588 | positions = [] 589 | for planet in planet_list: 590 | if planet != swe.KETU: 591 | nirayana_long = sidereal_longitude(jd_utc, planet) 592 | else: # Ketu 593 | nirayana_long = ketu(sidereal_longitude(jd_utc, swe.RAHU)) 594 | 595 | positions.append([planet, navamsa_from_long(nirayana_long)]) 596 | 597 | return positions 598 | 599 | # ----- TESTS ------ 600 | def all_tests(): 601 | print(sys._getframe().f_code.co_name) 602 | print(moonrise(date2, bangalore)) # Expected: 11:32:04 603 | print(moonset(date2, bangalore)) # Expected: 24:8:47 604 | print(sunrise(date2, bangalore)[1]) # Expected: 6:49:47 605 | print(sunset(date2, bangalore)[1]) # Expected: 18:10:25 606 | assert(vaara(date2) == 5) 607 | print(sunrise(date4, shillong)[1]) # On this day, Nakshatra and Yoga are skipped! 608 | assert(karana(date2, helsinki) == [14]) # Expected: 14, Vanija 609 | return 610 | 611 | def tithi_tests(): 612 | print(sys._getframe().f_code.co_name) 613 | feb3 = gregorian_to_jd(Date(2013, 2, 3)) 614 | apr24 = gregorian_to_jd(Date(2010, 4, 24)) 615 | apr19 = gregorian_to_jd(Date(2013, 4, 19)) 616 | apr20 = gregorian_to_jd(Date(2013, 4, 20)) 617 | apr21 = gregorian_to_jd(Date(2013, 4, 21)) 618 | print(tithi(date1, bangalore)) # Expected: krishna ashtami (23), ends at 27:07:38 619 | print(tithi(date2, bangalore)) # Expected: Saptami, ends at 16:24:19 620 | print(tithi(date3, bangalore)) # Expected: Krishna Saptami, ends at 25:03:30 621 | print(tithi(date2, helsinki)) # Expected: Shukla saptami until 12:54:19 622 | print(tithi(apr24, bangalore)) # Expected: [10, [6,9,29], 11, [27, 33, 58]] 623 | print(tithi(feb3, bangalore)) # Expected: [22, [8,14,6], 23, [30, 33, 17]] 624 | print(tithi(apr19, helsinki)) # Expected: [9, [28, 45, 0]] 625 | print(tithi(apr20, helsinki)) # Expected: [10, [29, 22, 7]] 626 | print(tithi(apr21, helsinki)) # Expected: [10, [5, 22, 6]] 627 | return 628 | 629 | def nakshatra_tests(): 630 | print(sys._getframe().f_code.co_name) 631 | print(nakshatra(date1, bangalore)) # Expected: 27 (Revati), ends at 17:06:37 632 | print(nakshatra(date2, bangalore)) # Expected: 27 (Revati), ends at 19:23:09 633 | print(nakshatra(date3, bangalore)) # Expecred: 24 (Shatabhisha) ends at 26:32:43 634 | print(nakshatra(date4, shillong)) # Expected: [3, [5,1,14]] then [4,[26,31,13]] 635 | return 636 | 637 | def yoga_tests(): 638 | print(sys._getframe().f_code.co_name) 639 | may22 = gregorian_to_jd(Date(2013, 5, 22)) 640 | print(yoga(date3, bangalore)) # Expected: Vishkambha (1), ends at 22:59:45 641 | print(yoga(date2, bangalore)) # Expected: Siddha (21), ends at 29:10:56 642 | print(yoga(may22, helsinki)) # [16, [6,20,33], 17, [27,21,58]] 643 | 644 | def masa_tests(): 645 | print(sys._getframe().f_code.co_name) 646 | jd = gregorian_to_jd(Date(2013, 2, 10)) 647 | aug17 = gregorian_to_jd(Date(2012, 8, 17)) 648 | aug18 = gregorian_to_jd(Date(2012, 8, 18)) 649 | sep19 = gregorian_to_jd(Date(2012, 9, 18)) 650 | may20 = gregorian_to_jd(Date(2012, 5, 20)) 651 | may21 = gregorian_to_jd(Date(2012, 5, 21)) 652 | print(masa(jd, bangalore)) # Pusya (10) 653 | print(masa(aug17, bangalore)) # Shravana (5) amavasya 654 | print(masa(aug18, bangalore)) # Adhika Bhadrapada [6, True] 655 | print(masa(sep19, bangalore)) # Normal Bhadrapada [6, False] 656 | print(masa(may20, helsinki)) # Vaisakha [2] 657 | print(masa(may21, helsinki)) # Jyestha [3] 658 | 659 | def ascendant_tests(): 660 | print(sys._getframe().f_code.co_name) 661 | jd = swe.julday(2015, 9, 24, 23 + 38/60.) 662 | assert(ascendant(jd, bangalore) == [2, [4, 37, 10], [5, 4]]) 663 | jd = swe.julday(2015, 9, 25, 13 + 29/60. + 13/3600.) 664 | assert(ascendant(jd, bangalore) == [8, [20, 23, 31], [20, 3]]) 665 | 666 | def navamsa_tests(): 667 | print(sys._getframe().f_code.co_name) 668 | jd = swe.julday(2015, 9, 25, 13 + 29/60. + 13/3600.) 669 | nv = navamsa(jd, bangalore) 670 | expected = [[0, 11], [1, 5], [4, 1], [2, 2], [5, 4], [3, 10], 671 | [6, 4], [10, 11], [9, 5], [7, 10], [8, 10]] 672 | assert(nv == expected) 673 | 674 | 675 | if __name__ == "__main__": 676 | import sys 677 | bangalore = Place(12.972, 77.594, +5.5) 678 | shillong = Place(25.569, 91.883, +5.5) 679 | helsinki = Place(60.17, 24.935, +2.0) 680 | date1 = gregorian_to_jd(Date(2009, 7, 15)) 681 | date2 = gregorian_to_jd(Date(2013, 1, 18)) 682 | date3 = gregorian_to_jd(Date(1985, 6, 9)) 683 | date4 = gregorian_to_jd(Date(2009, 6, 21)) 684 | apr_8 = gregorian_to_jd(Date(2010, 4, 8)) 685 | apr_10 = gregorian_to_jd(Date(2010, 4, 10)) 686 | all_tests() 687 | tithi_tests() 688 | nakshatra_tests() 689 | yoga_tests() 690 | masa_tests() 691 | ascendant_tests() 692 | navamsa_tests() 693 | # new_moon(jd) 694 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | drik-panchanga is a Python program to compute dates according to the Indian 3 | luni-solar calendar. 4 | Copyright (C) 2010-2060 Satish BD 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | The complete license text is given below 19 | 20 | ## License Text ## 21 | 22 | GNU AFFERO GENERAL PUBLIC LICENSE 23 | Version 3, 19 November 2007 24 | 25 | Copyright (C) 2007 Free Software Foundation, Inc. 26 | Everyone is permitted to copy and distribute verbatim copies 27 | of this license document, but changing it is not allowed. 28 | 29 | Preamble 30 | 31 | The GNU Affero General Public License is a free, copyleft license for 32 | software and other kinds of works, specifically designed to ensure 33 | cooperation with the community in the case of network server software. 34 | 35 | The licenses for most software and other practical works are designed 36 | to take away your freedom to share and change the works. By contrast, 37 | our General Public Licenses are intended to guarantee your freedom to 38 | share and change all versions of a program--to make sure it remains free 39 | software for all its users. 40 | 41 | When we speak of free software, we are referring to freedom, not 42 | price. Our General Public Licenses are designed to make sure that you 43 | have the freedom to distribute copies of free software (and charge for 44 | them if you wish), that you receive source code or can get it if you 45 | want it, that you can change the software or use pieces of it in new 46 | free programs, and that you know you can do these things. 47 | 48 | Developers that use our General Public Licenses protect your rights 49 | with two steps: (1) assert copyright on the software, and (2) offer 50 | you this License which gives you legal permission to copy, distribute 51 | and/or modify the software. 52 | 53 | A secondary benefit of defending all users' freedom is that 54 | improvements made in alternate versions of the program, if they 55 | receive widespread use, become available for other developers to 56 | incorporate. Many developers of free software are heartened and 57 | encouraged by the resulting cooperation. However, in the case of 58 | software used on network servers, this result may fail to come about. 59 | The GNU General Public License permits making a modified version and 60 | letting the public access it on a server without ever releasing its 61 | source code to the public. 62 | 63 | The GNU Affero General Public License is designed specifically to 64 | ensure that, in such cases, the modified source code becomes available 65 | to the community. It requires the operator of a network server to 66 | provide the source code of the modified version running there to the 67 | users of that server. Therefore, public use of a modified version, on 68 | a publicly accessible server, gives the public access to the source 69 | code of the modified version. 70 | 71 | An older license, called the Affero General Public License and 72 | published by Affero, was designed to accomplish similar goals. This is 73 | a different license, not a version of the Affero GPL, but Affero has 74 | released a new version of the Affero GPL which permits relicensing under 75 | this license. 76 | 77 | The precise terms and conditions for copying, distribution and 78 | modification follow. 79 | 80 | TERMS AND CONDITIONS 81 | 82 | 0. Definitions. 83 | 84 | "This License" refers to version 3 of the GNU Affero General Public License. 85 | 86 | "Copyright" also means copyright-like laws that apply to other kinds of 87 | works, such as semiconductor masks. 88 | 89 | "The Program" refers to any copyrightable work licensed under this 90 | License. Each licensee is addressed as "you". "Licensees" and 91 | "recipients" may be individuals or organizations. 92 | 93 | To "modify" a work means to copy from or adapt all or part of the work 94 | in a fashion requiring copyright permission, other than the making of an 95 | exact copy. The resulting work is called a "modified version" of the 96 | earlier work or a work "based on" the earlier work. 97 | 98 | A "covered work" means either the unmodified Program or a work based 99 | on the Program. 100 | 101 | To "propagate" a work means to do anything with it that, without 102 | permission, would make you directly or secondarily liable for 103 | infringement under applicable copyright law, except executing it on a 104 | computer or modifying a private copy. Propagation includes copying, 105 | distribution (with or without modification), making available to the 106 | public, and in some countries other activities as well. 107 | 108 | To "convey" a work means any kind of propagation that enables other 109 | parties to make or receive copies. Mere interaction with a user through 110 | a computer network, with no transfer of a copy, is not conveying. 111 | 112 | An interactive user interface displays "Appropriate Legal Notices" 113 | to the extent that it includes a convenient and prominently visible 114 | feature that (1) displays an appropriate copyright notice, and (2) 115 | tells the user that there is no warranty for the work (except to the 116 | extent that warranties are provided), that licensees may convey the 117 | work under this License, and how to view a copy of this License. If 118 | the interface presents a list of user commands or options, such as a 119 | menu, a prominent item in the list meets this criterion. 120 | 121 | 1. Source Code. 122 | 123 | The "source code" for a work means the preferred form of the work 124 | for making modifications to it. "Object code" means any non-source 125 | form of a work. 126 | 127 | A "Standard Interface" means an interface that either is an official 128 | standard defined by a recognized standards body, or, in the case of 129 | interfaces specified for a particular programming language, one that 130 | is widely used among developers working in that language. 131 | 132 | The "System Libraries" of an executable work include anything, other 133 | than the work as a whole, that (a) is included in the normal form of 134 | packaging a Major Component, but which is not part of that Major 135 | Component, and (b) serves only to enable use of the work with that 136 | Major Component, or to implement a Standard Interface for which an 137 | implementation is available to the public in source code form. A 138 | "Major Component", in this context, means a major essential component 139 | (kernel, window system, and so on) of the specific operating system 140 | (if any) on which the executable work runs, or a compiler used to 141 | produce the work, or an object code interpreter used to run it. 142 | 143 | The "Corresponding Source" for a work in object code form means all 144 | the source code needed to generate, install, and (for an executable 145 | work) run the object code and to modify the work, including scripts to 146 | control those activities. However, it does not include the work's 147 | System Libraries, or general-purpose tools or generally available free 148 | programs which are used unmodified in performing those activities but 149 | which are not part of the work. For example, Corresponding Source 150 | includes interface definition files associated with source files for 151 | the work, and the source code for shared libraries and dynamically 152 | linked subprograms that the work is specifically designed to require, 153 | such as by intimate data communication or control flow between those 154 | subprograms and other parts of the work. 155 | 156 | The Corresponding Source need not include anything that users 157 | can regenerate automatically from other parts of the Corresponding 158 | Source. 159 | 160 | The Corresponding Source for a work in source code form is that 161 | same work. 162 | 163 | 2. Basic Permissions. 164 | 165 | All rights granted under this License are granted for the term of 166 | copyright on the Program, and are irrevocable provided the stated 167 | conditions are met. This License explicitly affirms your unlimited 168 | permission to run the unmodified Program. The output from running a 169 | covered work is covered by this License only if the output, given its 170 | content, constitutes a covered work. This License acknowledges your 171 | rights of fair use or other equivalent, as provided by copyright law. 172 | 173 | You may make, run and propagate covered works that you do not 174 | convey, without conditions so long as your license otherwise remains 175 | in force. You may convey covered works to others for the sole purpose 176 | of having them make modifications exclusively for you, or provide you 177 | with facilities for running those works, provided that you comply with 178 | the terms of this License in conveying all material for which you do 179 | not control copyright. Those thus making or running the covered works 180 | for you must do so exclusively on your behalf, under your direction 181 | and control, on terms that prohibit them from making any copies of 182 | your copyrighted material outside their relationship with you. 183 | 184 | Conveying under any other circumstances is permitted solely under 185 | the conditions stated below. Sublicensing is not allowed; section 10 186 | makes it unnecessary. 187 | 188 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 189 | 190 | No covered work shall be deemed part of an effective technological 191 | measure under any applicable law fulfilling obligations under article 192 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 193 | similar laws prohibiting or restricting circumvention of such 194 | measures. 195 | 196 | When you convey a covered work, you waive any legal power to forbid 197 | circumvention of technological measures to the extent such circumvention 198 | is effected by exercising rights under this License with respect to 199 | the covered work, and you disclaim any intention to limit operation or 200 | modification of the work as a means of enforcing, against the work's 201 | users, your or third parties' legal rights to forbid circumvention of 202 | technological measures. 203 | 204 | 4. Conveying Verbatim Copies. 205 | 206 | You may convey verbatim copies of the Program's source code as you 207 | receive it, in any medium, provided that you conspicuously and 208 | appropriately publish on each copy an appropriate copyright notice; 209 | keep intact all notices stating that this License and any 210 | non-permissive terms added in accord with section 7 apply to the code; 211 | keep intact all notices of the absence of any warranty; and give all 212 | recipients a copy of this License along with the Program. 213 | 214 | You may charge any price or no price for each copy that you convey, 215 | and you may offer support or warranty protection for a fee. 216 | 217 | 5. Conveying Modified Source Versions. 218 | 219 | You may convey a work based on the Program, or the modifications to 220 | produce it from the Program, in the form of source code under the 221 | terms of section 4, provided that you also meet all of these conditions: 222 | 223 | a) The work must carry prominent notices stating that you modified 224 | it, and giving a relevant date. 225 | 226 | b) The work must carry prominent notices stating that it is 227 | released under this License and any conditions added under section 228 | 7. This requirement modifies the requirement in section 4 to 229 | "keep intact all notices". 230 | 231 | c) You must license the entire work, as a whole, under this 232 | License to anyone who comes into possession of a copy. This 233 | License will therefore apply, along with any applicable section 7 234 | additional terms, to the whole of the work, and all its parts, 235 | regardless of how they are packaged. This License gives no 236 | permission to license the work in any other way, but it does not 237 | invalidate such permission if you have separately received it. 238 | 239 | d) If the work has interactive user interfaces, each must display 240 | Appropriate Legal Notices; however, if the Program has interactive 241 | interfaces that do not display Appropriate Legal Notices, your 242 | work need not make them do so. 243 | 244 | A compilation of a covered work with other separate and independent 245 | works, which are not by their nature extensions of the covered work, 246 | and which are not combined with it such as to form a larger program, 247 | in or on a volume of a storage or distribution medium, is called an 248 | "aggregate" if the compilation and its resulting copyright are not 249 | used to limit the access or legal rights of the compilation's users 250 | beyond what the individual works permit. Inclusion of a covered work 251 | in an aggregate does not cause this License to apply to the other 252 | parts of the aggregate. 253 | 254 | 6. Conveying Non-Source Forms. 255 | 256 | You may convey a covered work in object code form under the terms 257 | of sections 4 and 5, provided that you also convey the 258 | machine-readable Corresponding Source under the terms of this License, 259 | in one of these ways: 260 | 261 | a) Convey the object code in, or embodied in, a physical product 262 | (including a physical distribution medium), accompanied by the 263 | Corresponding Source fixed on a durable physical medium 264 | customarily used for software interchange. 265 | 266 | b) Convey the object code in, or embodied in, a physical product 267 | (including a physical distribution medium), accompanied by a 268 | written offer, valid for at least three years and valid for as 269 | long as you offer spare parts or customer support for that product 270 | model, to give anyone who possesses the object code either (1) a 271 | copy of the Corresponding Source for all the software in the 272 | product that is covered by this License, on a durable physical 273 | medium customarily used for software interchange, for a price no 274 | more than your reasonable cost of physically performing this 275 | conveying of source, or (2) access to copy the 276 | Corresponding Source from a network server at no charge. 277 | 278 | c) Convey individual copies of the object code with a copy of the 279 | written offer to provide the Corresponding Source. This 280 | alternative is allowed only occasionally and noncommercially, and 281 | only if you received the object code with such an offer, in accord 282 | with subsection 6b. 283 | 284 | d) Convey the object code by offering access from a designated 285 | place (gratis or for a charge), and offer equivalent access to the 286 | Corresponding Source in the same way through the same place at no 287 | further charge. You need not require recipients to copy the 288 | Corresponding Source along with the object code. If the place to 289 | copy the object code is a network server, the Corresponding Source 290 | may be on a different server (operated by you or a third party) 291 | that supports equivalent copying facilities, provided you maintain 292 | clear directions next to the object code saying where to find the 293 | Corresponding Source. Regardless of what server hosts the 294 | Corresponding Source, you remain obligated to ensure that it is 295 | available for as long as needed to satisfy these requirements. 296 | 297 | e) Convey the object code using peer-to-peer transmission, provided 298 | you inform other peers where the object code and Corresponding 299 | Source of the work are being offered to the general public at no 300 | charge under subsection 6d. 301 | 302 | A separable portion of the object code, whose source code is excluded 303 | from the Corresponding Source as a System Library, need not be 304 | included in conveying the object code work. 305 | 306 | A "User Product" is either (1) a "consumer product", which means any 307 | tangible personal property which is normally used for personal, family, 308 | or household purposes, or (2) anything designed or sold for incorporation 309 | into a dwelling. In determining whether a product is a consumer product, 310 | doubtful cases shall be resolved in favor of coverage. For a particular 311 | product received by a particular user, "normally used" refers to a 312 | typical or common use of that class of product, regardless of the status 313 | of the particular user or of the way in which the particular user 314 | actually uses, or expects or is expected to use, the product. A product 315 | is a consumer product regardless of whether the product has substantial 316 | commercial, industrial or non-consumer uses, unless such uses represent 317 | the only significant mode of use of the product. 318 | 319 | "Installation Information" for a User Product means any methods, 320 | procedures, authorization keys, or other information required to install 321 | and execute modified versions of a covered work in that User Product from 322 | a modified version of its Corresponding Source. The information must 323 | suffice to ensure that the continued functioning of the modified object 324 | code is in no case prevented or interfered with solely because 325 | modification has been made. 326 | 327 | If you convey an object code work under this section in, or with, or 328 | specifically for use in, a User Product, and the conveying occurs as 329 | part of a transaction in which the right of possession and use of the 330 | User Product is transferred to the recipient in perpetuity or for a 331 | fixed term (regardless of how the transaction is characterized), the 332 | Corresponding Source conveyed under this section must be accompanied 333 | by the Installation Information. But this requirement does not apply 334 | if neither you nor any third party retains the ability to install 335 | modified object code on the User Product (for example, the work has 336 | been installed in ROM). 337 | 338 | The requirement to provide Installation Information does not include a 339 | requirement to continue to provide support service, warranty, or updates 340 | for a work that has been modified or installed by the recipient, or for 341 | the User Product in which it has been modified or installed. Access to a 342 | network may be denied when the modification itself materially and 343 | adversely affects the operation of the network or violates the rules and 344 | protocols for communication across the network. 345 | 346 | Corresponding Source conveyed, and Installation Information provided, 347 | in accord with this section must be in a format that is publicly 348 | documented (and with an implementation available to the public in 349 | source code form), and must require no special password or key for 350 | unpacking, reading or copying. 351 | 352 | 7. Additional Terms. 353 | 354 | "Additional permissions" are terms that supplement the terms of this 355 | License by making exceptions from one or more of its conditions. 356 | Additional permissions that are applicable to the entire Program shall 357 | be treated as though they were included in this License, to the extent 358 | that they are valid under applicable law. If additional permissions 359 | apply only to part of the Program, that part may be used separately 360 | under those permissions, but the entire Program remains governed by 361 | this License without regard to the additional permissions. 362 | 363 | When you convey a copy of a covered work, you may at your option 364 | remove any additional permissions from that copy, or from any part of 365 | it. (Additional permissions may be written to require their own 366 | removal in certain cases when you modify the work.) You may place 367 | additional permissions on material, added by you to a covered work, 368 | for which you have or can give appropriate copyright permission. 369 | 370 | Notwithstanding any other provision of this License, for material you 371 | add to a covered work, you may (if authorized by the copyright holders of 372 | that material) supplement the terms of this License with terms: 373 | 374 | a) Disclaiming warranty or limiting liability differently from the 375 | terms of sections 15 and 16 of this License; or 376 | 377 | b) Requiring preservation of specified reasonable legal notices or 378 | author attributions in that material or in the Appropriate Legal 379 | Notices displayed by works containing it; or 380 | 381 | c) Prohibiting misrepresentation of the origin of that material, or 382 | requiring that modified versions of such material be marked in 383 | reasonable ways as different from the original version; or 384 | 385 | d) Limiting the use for publicity purposes of names of licensors or 386 | authors of the material; or 387 | 388 | e) Declining to grant rights under trademark law for use of some 389 | trade names, trademarks, or service marks; or 390 | 391 | f) Requiring indemnification of licensors and authors of that 392 | material by anyone who conveys the material (or modified versions of 393 | it) with contractual assumptions of liability to the recipient, for 394 | any liability that these contractual assumptions directly impose on 395 | those licensors and authors. 396 | 397 | All other non-permissive additional terms are considered "further 398 | restrictions" within the meaning of section 10. If the Program as you 399 | received it, or any part of it, contains a notice stating that it is 400 | governed by this License along with a term that is a further 401 | restriction, you may remove that term. If a license document contains 402 | a further restriction but permits relicensing or conveying under this 403 | License, you may add to a covered work material governed by the terms 404 | of that license document, provided that the further restriction does 405 | not survive such relicensing or conveying. 406 | 407 | If you add terms to a covered work in accord with this section, you 408 | must place, in the relevant source files, a statement of the 409 | additional terms that apply to those files, or a notice indicating 410 | where to find the applicable terms. 411 | 412 | Additional terms, permissive or non-permissive, may be stated in the 413 | form of a separately written license, or stated as exceptions; 414 | the above requirements apply either way. 415 | 416 | 8. Termination. 417 | 418 | You may not propagate or modify a covered work except as expressly 419 | provided under this License. Any attempt otherwise to propagate or 420 | modify it is void, and will automatically terminate your rights under 421 | this License (including any patent licenses granted under the third 422 | paragraph of section 11). 423 | 424 | However, if you cease all violation of this License, then your 425 | license from a particular copyright holder is reinstated (a) 426 | provisionally, unless and until the copyright holder explicitly and 427 | finally terminates your license, and (b) permanently, if the copyright 428 | holder fails to notify you of the violation by some reasonable means 429 | prior to 60 days after the cessation. 430 | 431 | Moreover, your license from a particular copyright holder is 432 | reinstated permanently if the copyright holder notifies you of the 433 | violation by some reasonable means, this is the first time you have 434 | received notice of violation of this License (for any work) from that 435 | copyright holder, and you cure the violation prior to 30 days after 436 | your receipt of the notice. 437 | 438 | Termination of your rights under this section does not terminate the 439 | licenses of parties who have received copies or rights from you under 440 | this License. If your rights have been terminated and not permanently 441 | reinstated, you do not qualify to receive new licenses for the same 442 | material under section 10. 443 | 444 | 9. Acceptance Not Required for Having Copies. 445 | 446 | You are not required to accept this License in order to receive or 447 | run a copy of the Program. Ancillary propagation of a covered work 448 | occurring solely as a consequence of using peer-to-peer transmission 449 | to receive a copy likewise does not require acceptance. However, 450 | nothing other than this License grants you permission to propagate or 451 | modify any covered work. These actions infringe copyright if you do 452 | not accept this License. Therefore, by modifying or propagating a 453 | covered work, you indicate your acceptance of this License to do so. 454 | 455 | 10. Automatic Licensing of Downstream Recipients. 456 | 457 | Each time you convey a covered work, the recipient automatically 458 | receives a license from the original licensors, to run, modify and 459 | propagate that work, subject to this License. You are not responsible 460 | for enforcing compliance by third parties with this License. 461 | 462 | An "entity transaction" is a transaction transferring control of an 463 | organization, or substantially all assets of one, or subdividing an 464 | organization, or merging organizations. If propagation of a covered 465 | work results from an entity transaction, each party to that 466 | transaction who receives a copy of the work also receives whatever 467 | licenses to the work the party's predecessor in interest had or could 468 | give under the previous paragraph, plus a right to possession of the 469 | Corresponding Source of the work from the predecessor in interest, if 470 | the predecessor has it or can get it with reasonable efforts. 471 | 472 | You may not impose any further restrictions on the exercise of the 473 | rights granted or affirmed under this License. For example, you may 474 | not impose a license fee, royalty, or other charge for exercise of 475 | rights granted under this License, and you may not initiate litigation 476 | (including a cross-claim or counterclaim in a lawsuit) alleging that 477 | any patent claim is infringed by making, using, selling, offering for 478 | sale, or importing the Program or any portion of it. 479 | 480 | 11. Patents. 481 | 482 | A "contributor" is a copyright holder who authorizes use under this 483 | License of the Program or a work on which the Program is based. The 484 | work thus licensed is called the contributor's "contributor version". 485 | 486 | A contributor's "essential patent claims" are all patent claims 487 | owned or controlled by the contributor, whether already acquired or 488 | hereafter acquired, that would be infringed by some manner, permitted 489 | by this License, of making, using, or selling its contributor version, 490 | but do not include claims that would be infringed only as a 491 | consequence of further modification of the contributor version. For 492 | purposes of this definition, "control" includes the right to grant 493 | patent sublicenses in a manner consistent with the requirements of 494 | this License. 495 | 496 | Each contributor grants you a non-exclusive, worldwide, royalty-free 497 | patent license under the contributor's essential patent claims, to 498 | make, use, sell, offer for sale, import and otherwise run, modify and 499 | propagate the contents of its contributor version. 500 | 501 | In the following three paragraphs, a "patent license" is any express 502 | agreement or commitment, however denominated, not to enforce a patent 503 | (such as an express permission to practice a patent or covenant not to 504 | sue for patent infringement). To "grant" such a patent license to a 505 | party means to make such an agreement or commitment not to enforce a 506 | patent against the party. 507 | 508 | If you convey a covered work, knowingly relying on a patent license, 509 | and the Corresponding Source of the work is not available for anyone 510 | to copy, free of charge and under the terms of this License, through a 511 | publicly available network server or other readily accessible means, 512 | then you must either (1) cause the Corresponding Source to be so 513 | available, or (2) arrange to deprive yourself of the benefit of the 514 | patent license for this particular work, or (3) arrange, in a manner 515 | consistent with the requirements of this License, to extend the patent 516 | license to downstream recipients. "Knowingly relying" means you have 517 | actual knowledge that, but for the patent license, your conveying the 518 | covered work in a country, or your recipient's use of the covered work 519 | in a country, would infringe one or more identifiable patents in that 520 | country that you have reason to believe are valid. 521 | 522 | If, pursuant to or in connection with a single transaction or 523 | arrangement, you convey, or propagate by procuring conveyance of, a 524 | covered work, and grant a patent license to some of the parties 525 | receiving the covered work authorizing them to use, propagate, modify 526 | or convey a specific copy of the covered work, then the patent license 527 | you grant is automatically extended to all recipients of the covered 528 | work and works based on it. 529 | 530 | A patent license is "discriminatory" if it does not include within 531 | the scope of its coverage, prohibits the exercise of, or is 532 | conditioned on the non-exercise of one or more of the rights that are 533 | specifically granted under this License. You may not convey a covered 534 | work if you are a party to an arrangement with a third party that is 535 | in the business of distributing software, under which you make payment 536 | to the third party based on the extent of your activity of conveying 537 | the work, and under which the third party grants, to any of the 538 | parties who would receive the covered work from you, a discriminatory 539 | patent license (a) in connection with copies of the covered work 540 | conveyed by you (or copies made from those copies), or (b) primarily 541 | for and in connection with specific products or compilations that 542 | contain the covered work, unless you entered into that arrangement, 543 | or that patent license was granted, prior to 28 March 2007. 544 | 545 | Nothing in this License shall be construed as excluding or limiting 546 | any implied license or other defenses to infringement that may 547 | otherwise be available to you under applicable patent law. 548 | 549 | 12. No Surrender of Others' Freedom. 550 | 551 | If conditions are imposed on you (whether by court order, agreement or 552 | otherwise) that contradict the conditions of this License, they do not 553 | excuse you from the conditions of this License. If you cannot convey a 554 | covered work so as to satisfy simultaneously your obligations under this 555 | License and any other pertinent obligations, then as a consequence you may 556 | not convey it at all. For example, if you agree to terms that obligate you 557 | to collect a royalty for further conveying from those to whom you convey 558 | the Program, the only way you could satisfy both those terms and this 559 | License would be to refrain entirely from conveying the Program. 560 | 561 | 13. Remote Network Interaction; Use with the GNU General Public License. 562 | 563 | Notwithstanding any other provision of this License, if you modify the 564 | Program, your modified version must prominently offer all users 565 | interacting with it remotely through a computer network (if your version 566 | supports such interaction) an opportunity to receive the Corresponding 567 | Source of your version by providing access to the Corresponding Source 568 | from a network server at no charge, through some standard or customary 569 | means of facilitating copying of software. This Corresponding Source 570 | shall include the Corresponding Source for any work covered by version 3 571 | of the GNU General Public License that is incorporated pursuant to the 572 | following paragraph. 573 | 574 | Notwithstanding any other provision of this License, you have 575 | permission to link or combine any covered work with a work licensed 576 | under version 3 of the GNU General Public License into a single 577 | combined work, and to convey the resulting work. The terms of this 578 | License will continue to apply to the part which is the covered work, 579 | but the work with which it is combined will remain governed by version 580 | 3 of the GNU General Public License. 581 | 582 | 14. Revised Versions of this License. 583 | 584 | The Free Software Foundation may publish revised and/or new versions of 585 | the GNU Affero General Public License from time to time. Such new versions 586 | will be similar in spirit to the present version, but may differ in detail to 587 | address new problems or concerns. 588 | 589 | Each version is given a distinguishing version number. If the 590 | Program specifies that a certain numbered version of the GNU Affero General 591 | Public License "or any later version" applies to it, you have the 592 | option of following the terms and conditions either of that numbered 593 | version or of any later version published by the Free Software 594 | Foundation. If the Program does not specify a version number of the 595 | GNU Affero General Public License, you may choose any version ever published 596 | by the Free Software Foundation. 597 | 598 | If the Program specifies that a proxy can decide which future 599 | versions of the GNU Affero General Public License can be used, that proxy's 600 | public statement of acceptance of a version permanently authorizes you 601 | to choose that version for the Program. 602 | 603 | Later license versions may give you additional or different 604 | permissions. However, no additional obligations are imposed on any 605 | author or copyright holder as a result of your choosing to follow a 606 | later version. 607 | 608 | 15. Disclaimer of Warranty. 609 | 610 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 611 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 612 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 613 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 614 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 615 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 616 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 617 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 618 | 619 | 16. Limitation of Liability. 620 | 621 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 622 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 623 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 624 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 625 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 626 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 627 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 628 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 629 | SUCH DAMAGES. 630 | 631 | 17. Interpretation of Sections 15 and 16. 632 | 633 | If the disclaimer of warranty and limitation of liability provided 634 | above cannot be given local legal effect according to their terms, 635 | reviewing courts shall apply local law that most closely approximates 636 | an absolute waiver of all civil liability in connection with the 637 | Program, unless a warranty or assumption of liability accompanies a 638 | copy of the Program in return for a fee. 639 | 640 | END OF TERMS AND CONDITIONS 641 | 642 | How to Apply These Terms to Your New Programs 643 | 644 | If you develop a new program, and you want it to be of the greatest 645 | possible use to the public, the best way to achieve this is to make it 646 | free software which everyone can redistribute and change under these terms. 647 | 648 | To do so, attach the following notices to the program. It is safest 649 | to attach them to the start of each source file to most effectively 650 | state the exclusion of warranty; and each file should have at least 651 | the "copyright" line and a pointer to where the full notice is found. 652 | 653 | 654 | Copyright (C) 655 | 656 | This program is free software: you can redistribute it and/or modify 657 | it under the terms of the GNU Affero General Public License as published 658 | by the Free Software Foundation, either version 3 of the License, or 659 | (at your option) any later version. 660 | 661 | This program is distributed in the hope that it will be useful, 662 | but WITHOUT ANY WARRANTY; without even the implied warranty of 663 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 664 | GNU Affero General Public License for more details. 665 | 666 | You should have received a copy of the GNU Affero General Public License 667 | along with this program. If not, see . 668 | 669 | Also add information on how to contact you by electronic and paper mail. 670 | 671 | If your software can interact with users remotely through a computer 672 | network, you should also make sure that it provides a way for users to 673 | get its source. For example, if your program is a web application, its 674 | interface could display a "Source" link that leads users to an archive 675 | of the code. There are many ways you could offer source, and different 676 | solutions will be better for different programs; see section 13 for the 677 | specific requirements. 678 | 679 | You should also get your employer (if you work as a programmer) or school, 680 | if any, to sign a "copyright disclaimer" for the program, if necessary. 681 | For more information on this, and how to apply and follow the GNU AGPL, see 682 | . 683 | --------------------------------------------------------------------------------