From 57310a38f28c75483a3fd4d8e7f376305339aa9d Mon Sep 17 00:00:00 2001 From: Bigsk <xiaxinzhe@xiaxinzhe.cn> Date: Sun, 27 Dec 2020 01:11:26 +0800 Subject: [PATCH] UPDATE --- GhinkAPI.py | 0 Server.py | 109 ++++++++++++ TCPServer.py | 0 libraries/Astronomy.py | 161 ++++++++++++++++++ libraries/Calendar.py | 0 .../__pycache__/Astronomy.cpython-36.pyc | Bin 0 -> 7984 bytes 6 files changed, 270 insertions(+) delete mode 100644 GhinkAPI.py create mode 100644 Server.py delete mode 100644 TCPServer.py create mode 100644 libraries/Astronomy.py delete mode 100644 libraries/Calendar.py create mode 100644 libraries/__pycache__/Astronomy.cpython-36.pyc diff --git a/GhinkAPI.py b/GhinkAPI.py deleted file mode 100644 index e69de29..0000000 diff --git a/Server.py b/Server.py new file mode 100644 index 0000000..57c19df --- /dev/null +++ b/Server.py @@ -0,0 +1,109 @@ +import socket,threading,os,time,math,json,sys,urllib,requests +sys.path.append('libraries\\') +from Astronomy import * +#-------------------------------------# +#Define Area +class APICore(object): + HTTPHead={ + '200':'HTTP/1.1 200 OK\r\nServer: SparkE/Alpha1.0\r\nContent-Type: application/json; charset=utf-8\r\n\r\n', + '400':'HTTP/1.1 400 BAD REQUEST\r\nServer: SparkE/Alpha1.0\r\nContent-Type: application/json; charset=utf-8\r\n\r\n', + '404':'HTTP/1.1 404 NOT FOUND\r\nServer: SparkE/Alpha1.0\r\nContent-Type: application/json; charset=utf-8\r\n\r\n' + } + def __init__(self): + self.__Log=[] + def Log(self,IP,Port,Type,Text): + Time=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()) + self.__Log.append((Time,IP,Port,Type,Text)) + print("["+Type+"]["+Time+"]["+IP+"]["+str(Port)+"]"+str(Text)) + def IsJson(self,text): + try: + json.loads(text) + return True + except: + return False + def Error400(self,sock,addr): + sock.send(self.HTTPHead['400'].encode("utf-8")) + Result=json.dumps({"info":"bad request","code":400}) + self.Log(addr[0],addr[1],"SEND",Result) + sock.send(str(Result).encode("utf-8")) + def SunAzEl(self,sock,addr): + sock.send(self.HTTPHead['200'].encode("utf-8")) + Result=json.dumps(Astronomy().calcSunAzEl()) + self.Log(addr[0],addr[1],"SEND",Result) + sock.send(str(Result).encode("utf-8")) +#-------------------------------------# +API=APICore() +#-------------------------------------# +RequestRecord={} +server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) +server.bind(("0.0.0.0",8000)) +server.listen() +def socket(sock,addr): + while True: + try: + data = sock.recv(1024) + API.Log(addr[0],addr[1],"RECV","Received a request.") + #print(data.decode("utf-8")) + if("HTTP/" in data.decode("utf-8")): + #HTTP Request. + HTTPRequestHead=data.decode("utf-8").split("\r\n")[0].split(" ")[1].split("/") + del HTTPRequestHead[0] + for i in range(0,len(HTTPRequestHead)): + HTTPRequestHead[i]=urllib.parse.unquote(HTTPRequestHead[i]) + API.Log(addr[0],addr[1],"RECV",HTTPRequestHead) + if(" / " in data.decode("utf-8")): + sock.send(API.HTTPHead['200'].encode("utf-8")) + Result=json.dumps({"wikis":"https://gitee.com/ghink/api/wikis"}) + API.Log(addr[0],addr[1],"SEND",Result) + sock.send(str(Result).encode("utf8")) + break + elif(" /SunAzEl" in data.decode("utf-8")): + if("GET " in data.decode("utf-8")): + try: + HTTPRequestHead[1] + if(API.IsJson(HTTPRequestHead[1])): + API.SunAzEl(sock,addr) + else: + API.Error400(sock,addr) + except: + API.SunAzEl(sock,addr) + elif("POST " in data.decode("utf-8")): + try: + HTTPRequestHead[1] + API.Error400(sock,addr) + except: + API.SunAzEl(sock,addr) + break + else: + sock.send(API.HTTPHead['404'].encode("utf8")) + Result=json.dumps({"info":"not found","code":404}) + API.Log(addr[0],addr[1],"SEND",Result) + sock.send(Result.encode("utf-8")) + break + else: + #TCP Request. + if(data.decode("utf-8")=="Help" or data.decode("utf-8")=="HELP" or data.decode("utf-8")=="help" or data.decode("utf-8")=="WIKI" or data.decode("utf-8")=="wiki" or data.decode("utf-8")=="wikis" or data.decode("utf-8")=="WIKIS" or data.decode("utf-8")=="?"): + Result=json.dumps({"wikis":"https://gitee.com/ghink/api/wikis"}) + API.Log(addr[0],addr[1],"SEND",Result) + sock.send(str(Result).encode("utf8")) + elif(data.decode("utf-8")=="SunAzEl"): + Result=json.dumps(Astronomy().calcSunAzEl()) + API.Log(addr[0],addr[1],"SEND",Result) + sock.send(str(Result).encode("utf-8")) + else: + Result=json.dumps({"info":"not found","code":404}) + API.Log(addr[0],addr[1],"SEND",Result) + sock.send(Result.encode("utf-8")) + except (ConnectionResetError,ConnectionAbortedError): + API.Log(addr[0],addr[1],"STATUS","Lost connection.") + break + except UnicodeDecodeError: + API.Log(addr[0],addr[1],"STATUS","Wrong code mode,must be utf-8.") + API.Log(addr[0],addr[1],"STATUS","Lost connection.") + break +def main(): + sock,addr=server.accept() + client_thread=threading.Thread(target=socket,args=(sock,addr)) + client_thread.start() +while True: + main() diff --git a/TCPServer.py b/TCPServer.py deleted file mode 100644 index e69de29..0000000 diff --git a/libraries/Astronomy.py b/libraries/Astronomy.py new file mode 100644 index 0000000..7ba8bc6 --- /dev/null +++ b/libraries/Astronomy.py @@ -0,0 +1,161 @@ +import os,time,math +#-------------------------------------# +class Astronomy(object): + def calcSunDeclination(self,Stamp=None,TimeZone=8): + MonthList = [ + {"name": 'January', "numdays": 31}, + {"name": 'February', "numdays": 28}, + {"name": 'March', "numdays": 31}, + {"name": 'April', "numdays": 30}, + {"name": 'May', "numdays": 31}, + {"name": 'June', "numdays": 30}, + {"name": 'July', "numdays": 31}, + {"name": 'August', "numdays": 31}, + {"name": 'September', "numdays": 30}, + {"name": 'October', "numdays": 31}, + {"name": 'November', "numdays": 30}, + {"name": 'December', "numdays": 31}, + ] + if(Stamp==None): + TimeStamp = time.time() + else: + TimeStamp = Stamp + Year=int(time.strftime("%Y",time.localtime(TimeStamp))) + Month=int(time.strftime("%m",time.localtime(TimeStamp))) + Day=int(time.strftime("%d",time.localtime(TimeStamp))) + Hour=int(time.strftime("%H",time.localtime(TimeStamp))) + Minute=int(time.strftime("%M",time.localtime(TimeStamp))) + Second=int(time.strftime("%S",time.localtime(TimeStamp))) + if(((Year % 4 == 0 and Year % 100 != 0) or Year % 400 == 0) and (Month == 2)): + if(Day > 29): + Day=29 + else: + if(Day > MonthList[Month-1]['numdays']): + Day = MonthList[Month-1]['numdays'] + if(Month <= 2): + Year -= 1 + Month += 12 + L0 = 280.46646 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (36000.76983 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (0.0003032)) + while(L0 > 360.0): + L0 -= 360.0 + while(L0 < 0.0): + L0 += 360.0 + return (math.degrees(math.asin(math.sin(math.radians(23 + (26 + ((21.448 - ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (46.8150 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (0.00059 - ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (0.001813)))) / 60.0)) / 60.0 + 0.00256 * math.cos(math.radians(1254 - 1934.136 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525))))) * math.sin(math.radians(L0 + math.sin(math.radians(357.52911 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (359995029 - 0.0001537 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525)))) * (1.914602 - ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (0.004817 + 0.000014 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525))) + math.sin(math.radians(357.52911 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (359995029 - 0.0001537 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525))) * 2) * (0.019993 - 0.000101 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525)) + math.sin(math.radians(357.52911 + ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525) * (359995029 - 0.0001537 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525))) * 3) * 0.000289 - 0.00569 - 0.00478 * math.sin(math.radians(1254 - 1934.136 * ((math.floor(365.25 * (Year + 4716)) + math.floor(30.6001 * (Month + 1)) + Day + 2 - math.floor(Year / 100) + math.floor(math.floor(Year / 100) / 4) - 1524.5 + (Hour * 60 + Minute + Second/60.0) / 1440.0 - TimeZone / 24 - 2451545) / 36525))))))) * 100 + 0.5) / 100.0 + def calcHourAngle(self,Stamp=None,Lon=120,TimeZone=8): + MonthList = [ + {"name": 'January', "numdays": 31}, + {"name": 'February', "numdays": 28}, + {"name": 'March', "numdays": 31}, + {"name": 'April', "numdays": 30}, + {"name": 'May', "numdays": 31}, + {"name": 'June', "numdays": 30}, + {"name": 'July', "numdays": 31}, + {"name": 'August', "numdays": 31}, + {"name": 'September', "numdays": 30}, + {"name": 'October', "numdays": 31}, + {"name": 'November', "numdays": 30}, + {"name": 'December', "numdays": 31}, + ] + if(Stamp==None): + TimeStamp = time.time() + else: + TimeStamp = Stamp + Year=int(time.strftime("%Y",time.localtime(TimeStamp))) + Month=int(time.strftime("%m",time.localtime(TimeStamp))) + Day=int(time.strftime("%d",time.localtime(TimeStamp))) + Hour=int(time.strftime("%H",time.localtime(TimeStamp))) + Minute=int(time.strftime("%M",time.localtime(TimeStamp))) + Second=int(time.strftime("%S",time.localtime(TimeStamp))) + if(((Year % 4 == 0 and Year % 100 != 0) or Year % 400 == 0) and (Month == 2)): + if(Day > 29): + Day=29 + else: + if(Day > MonthList[Month-1]['numdays']): + Day = MonthList[Month-1]['numdays'] + if(Month <= 2): + Year -= 1 + Month += 12 + TimeJulianCent = (math.floor(365.25*(Year + 4716)) + math.floor(30.6001*(Month+1)) + Day + 2 - math.floor(Year/100) + math.floor(math.floor(Year/100)/4) - 1524.5 + Hour * 60 + Minute + Second/60.0/1440.0 - TimeZone/24 - 2451545)/36525 + L0 = 280.46646 + TimeJulianCent * (36000.76983 + TimeJulianCent *(0.0003032)) + while(L0 > 360.0): + L0 -= 360.0 + while(L0 < 0.0): + L0 += 360.0 + TrueSolarTime = (Hour * 60 + Minute + Second / 60) + ((4 * math.degrees((math.tan(math.radians((23 + (26 + ((21.448 - TimeJulianCent * (46.8150 + TimeJulianCent * (0.00059 - TimeJulianCent * (0.001813)))) / 60.0)) / 60.0 + 0.00256 * math.cos(math.radians(1254 - 1934.136 * TimeJulianCent))))/2) ** 2) * math.sin(2 * math.radians(L0)) - 2 * (0.016708634 - TimeJulianCent * (0.000042037 + 0.0000001267 * TimeJulianCent)) * math.sin(math.radians(357.52911 + TimeJulianCent * (359995029 - 0.0001537 * TimeJulianCent))) + 4 * (0.016708634 - TimeJulianCent * (0.000042037 + 0.0000001267 * TimeJulianCent)) * (math.tan(math.radians((23 + (26 + ((21.448 - TimeJulianCent * (46.8150 + TimeJulianCent * (0.00059 - TimeJulianCent * (0.001813)))) / 60.0)) / 60.0 + 0.00256 * math.cos(math.radians(1254 - 1934.136 * TimeJulianCent)))) / 2) ** 2) * math.sin(math.radians(357.52911 + TimeJulianCent * (359995029 - 0.0001537 * TimeJulianCent))) * math.cos(2 * math.radians(L0)) - 0.5 * (math.tan(math.radians((23 + (26 + ((21.448 - TimeJulianCent * (46.8150 + TimeJulianCent * (0.00059 - TimeJulianCent * (0.001813)))) / 60.0)) / 60.0 + 0.00256 * math.cos(math.radians(1254 - 1934.136 * TimeJulianCent))))/2) ** 2) * (math.tan(math.radians((23 + (26 + ((21.448 - TimeJulianCent * (46.8150 + TimeJulianCent * (0.00059 - TimeJulianCent * (0.001813)))) / 60.0)) / 60.0 + 0.00256 * math.cos(math.radians(1254 - 1934.136 * TimeJulianCent))))/2) ** 2) * math.sin(4 * math.radians(L0)) - 1.25 * ((0.016708634 - TimeJulianCent * (0.000042037 + 0.0000001267 * TimeJulianCent)) ** 2) * math.sin(2 * math.radians(math.radians(357.52911 + TimeJulianCent * (359995029 - 0.0001537 * TimeJulianCent)))))) + 4 * Lon - 60.0 * TimeZone) + while (TrueSolarTime > 1440): + TrueSolarTime -= 1440 + HourAngle = TrueSolarTime / 4 - 180.0 + if (HourAngle < -180): + HourAngle += 360.0 + return HourAngle + def calcSunAzEl(self,Stamp=None,Lon=120,Lat=30,TimeZone=8,ZeroAzimuth="North"): + MonthList = [ + {"name": 'January', "numdays": 31}, + {"name": 'February', "numdays": 28}, + {"name": 'March', "numdays": 31}, + {"name": 'April', "numdays": 30}, + {"name": 'May', "numdays": 31}, + {"name": 'June', "numdays": 30}, + {"name": 'July', "numdays": 31}, + {"name": 'August', "numdays": 31}, + {"name": 'September', "numdays": 30}, + {"name": 'October', "numdays": 31}, + {"name": 'November', "numdays": 30}, + {"name": 'December', "numdays": 31}, + ] + if(Stamp==None): + TimeStamp = time.time() + else: + TimeStamp = Stamp + Year=int(time.strftime("%Y",time.localtime(TimeStamp))) + Month=int(time.strftime("%m",time.localtime(TimeStamp))) + Day=int(time.strftime("%d",time.localtime(TimeStamp))) + Hour=int(time.strftime("%H",time.localtime(TimeStamp))) + Minute=int(time.strftime("%M",time.localtime(TimeStamp))) + Second=int(time.strftime("%S",time.localtime(TimeStamp))) + if(((Year % 4 == 0 and Year % 100 != 0) or Year % 400 == 0) and (Month == 2)): + if(Day > 29): + Day=29 + else: + if(Day > MonthList[Month-1]['numdays']): + Day = MonthList[Month-1]['numdays'] + if(Month <= 2): + Year -= 1 + Month += 12 + ZeroAzimuth=180 if(ZeroAzimuth=="South") else 0 + csz = math.sin(math.radians(Lat)) * math.sin(math.radians(self.calcSunDeclination(Stamp,TimeZone))) + math.cos(math.radians(Lat)) * math.cos(math.radians(self.calcSunDeclination(Stamp,TimeZone))) * math.cos(math.radians(self.calcHourAngle(Stamp,Lon,TimeZone))) + if(csz > 1): + csz = 1 + elif(csz < -1): + csz = -1 + if(abs((math.cos(math.radians(Lat)) * math.sin(math.radians(math.degrees(math.acos(csz)))))) > 0.001): + azRad = ((math.sin(math.radians(Lat)) * math.cos(math.radians(math.degrees(math.acos(csz))))) - math.sin(math.radians(self.calcSunDeclination(Stamp,TimeZone)))) / (math.cos(math.radians(Lat)) * math.sin(math.radians(math.degrees(math.acos(csz))))) + if(abs(azRad) > 1.0): + if(azRad < 0): + azRad = -1.0 + else: + azRad = 1.0 + azimuth = 180.0 - math.degrees(math.acos(azRad)) + if(self.calcHourAngle(Stamp,Lon,TimeZone) > 0.0): + azimuth = -azimuth + else: + if(Lat > 0.0): + azimuth = 180.0 + else: + azimuth = 0.0 + if(azimuth < 0.0): + azimuth += 360.0 + exoatmElevation = (90.0 - math.degrees(math.acos(csz))) + if (exoatmElevation > 85.0): + refractionCorrection = 0.0 + else: + te = math.tan (math.radians(exoatmElevation)) + if (exoatmElevation > 5.0): + refractionCorrection = 58.1 / te - 0.07 / (te*te*te) + 0.000086 / (te*te*te*te*te) + elif (exoatmElevation > -0.575): + refractionCorrection = 1735.0 + exoatmElevation * (-518.2 + exoatmElevation * (103.4 + exoatmElevation * (-12.79 + exoatmElevation * 0.711) ) ) + else: + refractionCorrection = -20.774 / te + refractionCorrection = refractionCorrection / 3600.0 + solarZen = math.degrees(math.acos(csz)) - refractionCorrection + return {"az":((azimuth*100 +0.5) - (ZeroAzimuth*100))/100.0,"el":((90.0-solarZen)*100+0.5)/100.0} \ No newline at end of file diff --git a/libraries/Calendar.py b/libraries/Calendar.py deleted file mode 100644 index e69de29..0000000 diff --git a/libraries/__pycache__/Astronomy.cpython-36.pyc b/libraries/__pycache__/Astronomy.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41491c9e4241dd49eb165dc80311dace505ecec3 GIT binary patch literal 7984 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 Bx@!Oc literal 0 HcmV?d00001