def getEphemerisDataLineForDatetime(dt): """Obtains the line of CSV text of planetary position data. Arguments: dt - datetime.datetime object with the timestamp seeked. Returns: str in CSV format. Since there are a lot of fields, please See the section of code where we write the header info str for the format. """ # Return value. rv = "" planetaryInfos = getPlanetaryInfosForDatetime(dt) log.debug("Just obtained planetaryInfos for timestamp: {}".\ format(Ephemeris.datetimeToStr(dt))) # Planet geocentric longitude 15-degree axis points. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] rv += "{:.3f},".format(lon % 15.0) # Planet geocentric longitude. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] rv += "{:.3f},".format(lon) # Planet geocentric longitude in zodiac str format. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] valueStr = \ AstrologyUtils.\ convertLongitudeToStrWithRasiAbbrev(lon) rv += valueStr + "," # Planet heliocentric longitude 15-degree axis points. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] rv += "{:.3f},".format(lon % 15.0) # Planet heliocentric longitude. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] rv += "{:.3f},".format(lon) # Planet heliocentric longitude in zodiac str format. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] valueStr = \ AstrologyUtils.\ convertLongitudeToStrWithRasiAbbrev(lon) rv += valueStr + "," # Planet declination. for planetName in declinationPlanetNames: for pi in planetaryInfos: if pi.name == planetName: declination = pi.geocentric['tropical']['declination'] rv += "{:.3f},".format(declination) # Planet geocentric latitude. for planetName in geocentricLatitudePlanetNames: for pi in planetaryInfos: if pi.name == planetName: latitude = pi.geocentric['tropical']['latitude'] rv += "{:.3f},".format(latitude) # Planet heliocentric latitude. for planetName in heliocentricLatitudePlanetNames: for pi in planetaryInfos: if pi.name == planetName: latitude = pi.heliocentric['tropical']['latitude'] rv += "{:.3f},".format(latitude) # Remove trailing comma. rv = rv[:-1] return rv
headerLine += "CycleHit2P,CycleHit3P," headerLine += tacEphemerisHeaderFields + "," headerLine += genericEphemerisHeaderFields + "," # Remove the trailing comma. headerLine = headerLine[:-1] # Get the earliest date. earliestDt = getEarliestTimestampFromData(priceBars, tacEphemerisData, cycleHitDates2PlanetSystem, cycleHitDates3PlanetSystem, tacEphemerisData) log.debug("Earliest timestamp in all the files is: {}".\ format(Ephemeris.datetimeToStr(earliestDt))) latestDt = getLatestTimestampFromData(priceBars, tacEphemerisData, cycleHitDates2PlanetSystem, cycleHitDates3PlanetSystem, tacEphemerisData) log.debug("Latest timestamp in all the files is: {}".\ format(Ephemeris.datetimeToStr(latestDt))) startDt = earliestDt endDt = latestDt # Initialize the currDt to the start date. Manually set the hour and
def processPCDD(pcdd, tag): """Module for printing generic information about the PriceBars in a PriceChartDocumentData object. The generic information that is printed includes: - Earliest pricebar timestamp as a datetime.datetime and a julian day. - Latest pricebar timestamp as a datetime.datetime and a julian day. - highest pricebar high price. - lowest pricebar low price. Arguments: pcdd - PriceChartDocumentData object that will be modified. tag - str containing the tag. The value of this field may be "" if a tag is not specified by the user. This implementation doesn't use this field. Returns: 0 if the changes are to be saved to file. 1 if the changes are NOT to be saved to file. This implementation always returns 1. """ # Return value. rv = 1 # Get the number of PriceBars. numPriceBars = len(pcdd.priceBars) # Get the earliest and latest PriceBar. earliestPriceBar = None latestPriceBar = None lowestPrice = None highestPrice = None lowestClosePrice = None highestClosePrice = None for pb in pcdd.priceBars: if earliestPriceBar == None: earliestPriceBar = pb elif pb.timestamp < earliestPriceBar.timestamp: earliestPriceBar = pb if latestPriceBar == None: latestPriceBar = pb elif pb.timestamp > latestPriceBar.timestamp: latestPriceBar = pb if lowestPrice == None: lowestPrice = pb.low elif pb.low < lowestPrice: lowestPrice = pb.low if highestPrice == None: highestPrice = pb.high elif pb.high > highestPrice: highestPrice = pb.high if lowestClosePrice == None: lowestClosePrice = pb.close elif pb.close < lowestClosePrice: lowestClosePrice = pb.close if highestClosePrice == None: highestClosePrice = pb.close elif pb.close > highestClosePrice: highestClosePrice = pb.close log.info("") log.info("Number of PriceBars: {}".format(numPriceBars)) if numPriceBars > 0: # Make sure we got values for everything. if earliestPriceBar == None or \ latestPriceBar == None or \ lowestPrice == None or \ highestPrice == None or \ lowestClosePrice == None or \ highestClosePrice == None: log.error("PriceBars existed, but we are missing some set values.") rv = 1 return rv # Convert the datetimes to julian day. earliestPriceBarJd = \ Ephemeris.datetimeToJulianDay(earliestPriceBar.timestamp) latestPriceBarJd = \ Ephemeris.datetimeToJulianDay(latestPriceBar.timestamp) # Print the information to log. log.info("EarliestPriceBar datetime == {}".\ format(Ephemeris.datetimeToStr(earliestPriceBar.timestamp))) log.info("LatestPriceBar datetime == {}".\ format(Ephemeris.datetimeToStr(latestPriceBar.timestamp))) log.info("EarliestPriceBar julian day == {}".format(earliestPriceBarJd)) log.info("LatestPriceBar julian day == {}".format(latestPriceBarJd)) log.info("Lowest PriceBar LOW price == {}".format(highestPrice)) log.info("Highest PriceBar HIGH price == {}".format(highestPrice)) log.info("Lowest PriceBar CLOSE price == {}".format(highestPrice)) log.info("Highest PriceBar CLOSE price == {}".format(highestPrice)) log.info("") rv = 1 return rv
#if __name__ == "__main__": # Initialize Ephemeris (required). Ephemeris.initialize() # Set the Location (required). Ephemeris.setGeographicPosition(locationLongitude, locationLatitude, locationElevation) # Log the parameters that are being used. log.info("Location used is: {} (lat={}, lon={})".\ format(locationName, locationLatitude, locationLongitude)) log.info("Timezone used is: {}".format(timezone.zone)) log.info("Start timestamp: {}".format(Ephemeris.datetimeToStr(startDt))) log.info("End timestamp: {}".format(Ephemeris.datetimeToStr(endDt))) # Compile the header line text. headerLine = "" headerLine += "Date" + "," headerLine += "Day of week" + "," headerLine += "Day count" + "," headerLine += "Week count" + "," headerLine += "Month count" + "," # Planet geocentric longitude mod 15. for planetName in geocentricPlanetNames: headerLine += "G." + planetName + "%15" + "," # Planet geocentric longitude.
def processPCDD(pcdd, tag): """Module for drawing two veritcal lines (line segments) at 1/3 and 2/3 of the way through the price chart. The lines will have a tag str matching the given tag parameter. Arguments: pcdd - PriceChartDocumentData object that will be modified. tag - str containing the tag. Returns: 0 if the changes are to be saved to file. 1 if the changes are NOT to be saved to file. """ # Return value. rv = 0 # Check input. if tag == None or tag == "": log.error("Must specify a non-empty tag str value.") rv = 1 return rv # Get the earliest and latest PriceBar. earliestPriceBar = None latestPriceBar = None lowestPrice = None highestPrice = None for pb in pcdd.priceBars: if earliestPriceBar == None: earliestPriceBar = pb elif pb.timestamp < earliestPriceBar.timestamp: earliestPriceBar = pb if latestPriceBar == None: latestPriceBar = pb elif pb.timestamp > latestPriceBar.timestamp: latestPriceBar = pb if lowestPrice == None: lowestPrice = pb.low elif pb.low < lowestPrice: lowestPrice = pb.low if highestPrice == None: highestPrice = pb.high elif pb.high > highestPrice: highestPrice = pb.high if earliestPriceBar == None or \ latestPriceBar == None or \ lowestPrice == None or \ highestPrice == None: log.info("No pricebars were found in the document. " + \ "Not doing anything.") rv = 1 return rv # Convert the datetimes to julian day. earliestPriceBarJd = \ Ephemeris.datetimeToJulianDay(earliestPriceBar.timestamp) latestPriceBarJd = \ Ephemeris.datetimeToJulianDay(latestPriceBar.timestamp) log.debug("earliestPriceBar.timestamp == {}".\ format(Ephemeris.datetimeToStr(earliestPriceBar.timestamp))) log.debug("latestPriceBar.timestamp == {}".\ format(Ephemeris.datetimeToStr(latestPriceBar.timestamp))) log.debug("earliestPriceBarJd == {}".format(earliestPriceBarJd)) log.debug("latestPriceBarJd == {}".format(latestPriceBarJd)) diff = latestPriceBarJd - earliestPriceBarJd jdLine1 = earliestPriceBarJd + (diff / 3) jdLine2 = earliestPriceBarJd + ((diff / 3) * 2) dtLine1 = Ephemeris.julianDayToDatetime(jdLine1) dtLine2 = Ephemeris.julianDayToDatetime(jdLine2) log.debug("Creating scene...") # Scene for conversion functions. scene = PriceBarChartGraphicsScene() log.debug("Converting dt to x...") line1X = scene.datetimeToSceneXPos(dtLine1) line2X = scene.datetimeToSceneXPos(dtLine2) log.debug("Converting price to y...") lowY = scene.priceToSceneYPos(lowestPrice) highY = scene.priceToSceneYPos(highestPrice) log.debug("Creating line1 artifact ...") line1Artifact = PriceBarChartLineSegmentArtifact() line1Artifact.addTag(tag) line1Artifact.setTiltedTextFlag(False) line1Artifact.setAngleTextFlag(False) line1Artifact.setColor(QColor(Qt.red)) line1Artifact.setStartPointF(QPointF(line1X, lowY)) line1Artifact.setEndPointF(QPointF(line1X, highY)) log.debug("Creating line2 artifact ...") line2Artifact = PriceBarChartLineSegmentArtifact() line2Artifact.addTag(tag) line2Artifact.setTiltedTextFlag(False) line2Artifact.setAngleTextFlag(False) line2Artifact.setColor(QColor(Qt.blue)) line2Artifact.setStartPointF(QPointF(line2X, lowY)) line2Artifact.setEndPointF(QPointF(line2X, highY)) log.debug("Appending two lines...") pcdd.priceBarChartArtifacts.append(line1Artifact) pcdd.priceBarChartArtifacts.append(line2Artifact) log.debug("Done appending two lines.") rv = 0 return rv
def getLongitudeAspectTimestamps(\ startDt, endDt, planet1ParamsList, planet2ParamsList, degreeDifference, uniDirectionalAspectsFlag=False, maxErrorTd=datetime.timedelta(minutes=1)): """Obtains a list of datetime.datetime objects that contain the moments when the aspect specified is active. Warning on usage: When planet-longitude-averaging is utilized for the longitude of planet1 or planet2, the aspects returned by this function cannot be fully relied upon. This short-coming happens under these circumstances because it is possible that the longitude can abruptly 'jump' or hop a large distance when measurements are taken between timestamp steps. For example, this 'jumping' effect can occur if two planets A and B, are both around 355 degrees, and planet A crosses the 0 degree mark. Now the average goes from around 355 degrees (355 + 355 = 710 / 2 = 355), to about 180 degrees (355 + 0 = 355 / 2 = about 180). While corrections for this can be made for the case of having only 2 planets involved, if more planets are involved then the adjustment required quickly becomes non-trivial. Arguments: startDt - datetime.datetime object for the starting timestamp to do the calculations for artifacts. endDt - datetime.datetime object for the ending timestamp to do the calculations for artifacts. planet1ParamsList - List of tuples that will be used as parameters for planet1. Each tuple contained in this list represents parameters for each planet that will get averaged to create what is known as planet1. The contents of the tuple are: (planetName, centricityType, longitudeType) Where: planetName - str holding the name of the second planet to do the calculations for. centricityType - str value holding either "geocentric", "topocentric", or "heliocentric". longitudeType - str value holding either "tropical" or "sidereal". Example: So if someone wanted planet1 to be the average location of of geocentric sidereal Saturn and geocentric sidereal Uranus, the 'planet1ParamsList' parameter would be: [("Saturn", "geocentric", "sidereal"), ("Uranus", "geocentric", "sidereal")] If the typical use-case is desired for the longitude of just a single planet, pass a list with only 1 tuple. As an example, for Mercury it would be: [("Mercury", "heliocentric", "tropical")] planet2ParamsList - List of tuples that will be used as parameters for planet2. For additional details about the format of this parameter field, please see the description for parameter 'planet1ParamsList' degreeDifference - float value for the number of degrees of separation for this aspect. uniDirectionalAspectsFlag - bool value for whether or not uni-directional aspects are enabled or not. By default, aspects are bi-directional, so Saturn square-aspect Jupiter would be the same as Jupiter square-aspect Saturn. If this flag is set to True, then those two combinations would be considered unique. In the case where the flag is set to True, for the aspect to be active, planet2 would need to be 'degreeDifference' degrees in front of planet1. maxErrorTd - datetime.timedelta object holding the maximum time difference between the exact planetary combination timestamp, and the one calculated. This would define the accuracy of the calculations. Returns: List of datetime.datetime objects. Each timestamp in the list is the moment where the aspect is active and satisfies the given parameters. In the event of an error, the reference None is returned. """ log.debug("Entered " + inspect.stack()[0][3] + "()") # List of timestamps of the aspects found. aspectTimestamps = [] # Make sure the inputs are valid. if endDt < startDt: log.error("Invalid input: 'endDt' must be after 'startDt'") return None # Check to make sure planet lists were given. if len(planet1ParamsList) == 0: log.error("planet1ParamsList must contain at least 1 tuple.") return None if len(planet2ParamsList) == 0: log.error("planet2ParamsList must contain at least 1 tuple.") return None log.debug("planet1ParamsList passed in is: {}".\ format(planet1ParamsList)) log.debug("planet2ParamsList passed in is: {}".\ format(planet2ParamsList)) # Check for valid inputs in each of the planet parameter lists. for planetTuple in planet1ParamsList + planet2ParamsList: if len(planetTuple) != 3: log.error("Input error: " + \ "Not enough values given in planet tuple.") return None planetName = planetTuple[0] centricityType = planetTuple[1] longitudeType = planetTuple[2] loweredCentricityType = centricityType.lower() if loweredCentricityType != "geocentric" and \ loweredCentricityType != "topocentric" and \ loweredCentricityType != "heliocentric": log.error("Invalid input: Centricity type is invalid. " + \ "Value given was: {}".format(centricityType)) return None # Check inputs for longitude type. loweredLongitudeType = longitudeType.lower() if loweredLongitudeType != "tropical" and \ loweredLongitudeType != "sidereal": log.error("Invalid input: Longitude type is invalid. " + \ "Value given was: {}".format(longitudeType)) return None # Field name we are getting. fieldName = "longitude" # Initialize the Ephemeris with the birth location. log.debug("Setting ephemeris location ...") Ephemeris.setGeographicPosition(locationLongitude, locationLatitude, locationElevation) # Set the step size. stepSizeTd = datetime.timedelta(days=1) for planetTuple in planet1ParamsList + planet2ParamsList: planetName = planetTuple[0] if Ephemeris.isHouseCuspPlanetName(planetName) or \ Ephemeris.isAscmcPlanetName(planetName): # House cusps and ascmc planets need a smaller step size. stepSizeTd = datetime.timedelta(hours=1) elif planetName == "Moon": # Use a smaller step size for the moon so we can catch # smaller aspect sizes. stepSizeTd = datetime.timedelta(hours=3) log.debug("Step size is: {}".format(stepSizeTd)) # Desired angles. We need to check for planets at these angles. desiredAngleDegList = [] desiredAngleDeg1 = Util.toNormalizedAngle(degreeDifference) desiredAngleDegList.append(desiredAngleDeg1) if Util.fuzzyIsEqual(desiredAngleDeg1, 0): desiredAngleDegList.append(360) if uniDirectionalAspectsFlag == False: desiredAngleDeg2 = \ 360 - Util.toNormalizedAngle(degreeDifference) if desiredAngleDeg2 not in desiredAngleDegList: desiredAngleDegList.append(desiredAngleDeg2) # Debug output. anglesStr = "" for angle in desiredAngleDegList: anglesStr += "{} ".format(angle) log.debug("Angles in desiredAngleDegList: " + anglesStr) # Iterate through, appending to aspectTimestamps list as we go. steps = [] steps.append(copy.deepcopy(startDt)) steps.append(copy.deepcopy(startDt)) longitudesP1 = [] longitudesP1.append(None) longitudesP1.append(None) longitudesP2 = [] longitudesP2.append(None) longitudesP2.append(None) def getFieldValue(dt, planetParamsList, fieldName): """Creates the PlanetaryInfo object for the given planetParamsList and returns the value of the field desired. """ log.debug("planetParamsList passed in is: {}".\ format(planetParamsList)) unAveragedFieldValues = [] for t in planetParamsList: planetName = t[0] centricityType = t[1] longitudeType = t[2] pi = Ephemeris.getPlanetaryInfo(planetName, dt) fieldValue = None if centricityType.lower() == "geocentric": fieldValue = pi.geocentric[longitudeType][fieldName] elif centricityType.lower() == "topocentric": fieldValue = pi.topocentric[longitudeType][fieldName] elif centricityType.lower() == "heliocentric": fieldValue = pi.heliocentric[longitudeType][fieldName] else: log.error("Unknown centricity type: {}".\ format(centricityType)) fieldValue = None unAveragedFieldValues.append(fieldValue) log.debug("unAveragedFieldValues is: {}".\ format(unAveragedFieldValues)) # Average the field values. total = 0.0 for v in unAveragedFieldValues: total += v averagedFieldValue = total / len(unAveragedFieldValues) log.debug("averagedFieldValue is: {}".\ format(averagedFieldValue)) return averagedFieldValue log.debug("Stepping through timestamps from {} to {} ...".\ format(Ephemeris.datetimeToStr(startDt), Ephemeris.datetimeToStr(endDt))) currDiff = None prevDiff = None while steps[-1] < endDt: currDt = steps[-1] prevDt = steps[-2] log.debug("Looking at currDt == {} ...".\ format(Ephemeris.datetimeToStr(currDt))) longitudesP1[-1] = \ Util.toNormalizedAngle(\ getFieldValue(currDt, planet1ParamsList, fieldName)) longitudesP2[-1] = \ Util.toNormalizedAngle(\ getFieldValue(currDt, planet2ParamsList, fieldName)) log.debug("{} {} is: {}".\ format(planet1ParamsList, fieldName, longitudesP1[-1])) log.debug("{} {} is: {}".\ format(planet2ParamsList, fieldName, longitudesP2[-1])) currDiff = Util.toNormalizedAngle(\ longitudesP1[-1] - longitudesP2[-1]) log.debug("prevDiff == {}".format(prevDiff)) log.debug("currDiff == {}".format(currDiff)) if prevDiff != None and \ longitudesP1[-2] != None and \ longitudesP2[-2] != None: if abs(prevDiff - currDiff) > 180: # Probably crossed over 0. Adjust the prevDiff so # that the rest of the algorithm can continue to # work. if prevDiff > currDiff: prevDiff -= 360 else: prevDiff += 360 log.debug("After adjustment: prevDiff == {}".\ format(prevDiff)) log.debug("After adjustment: currDiff == {}".\ format(currDiff)) for desiredAngleDeg in desiredAngleDegList: log.debug("Looking at desiredAngleDeg: {}".\ format(desiredAngleDeg)) desiredDegree = desiredAngleDeg if prevDiff < desiredDegree and currDiff >= desiredDegree: log.debug("Crossed over {} from below to above!".\ format(desiredDegree)) # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than # the threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. timeWindowTd = t2 - t1 halfTimeWindowTd = \ datetime.\ timedelta(days=(timeWindowTd.days / 2.0), seconds=(timeWindowTd.seconds / 2.0), microseconds=\ (timeWindowTd.microseconds / 2.0)) testDt = t1 + halfTimeWindowTd testValueP1 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet1ParamsList, fieldName)) testValueP2 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet2ParamsList, fieldName)) log.debug("testValueP1 == {}".format(testValueP1)) log.debug("testValueP2 == {}".format(testValueP2)) if longitudesP1[-2] > 240 and testValueP1 < 120: # Planet 1 hopped over 0 degrees. testValueP1 += 360 elif longitudesP1[-2] < 120 and testValueP1 > 240: # Planet 1 hopped over 0 degrees. testValueP1 -= 360 if longitudesP2[-2] > 240 and testValueP2 < 120: # Planet 2 hopped over 0 degrees. testValueP2 += 360 elif longitudesP2[-2] < 120 and testValueP2 > 240: # Planet 2 hopped over 0 degrees. testValueP2 -= 360 testDiff = Util.toNormalizedAngle(\ testValueP1 - testValueP2) # Handle special cases of degrees 0 and 360. # Here we adjust testDiff so that it is in the # expected ranges. if Util.fuzzyIsEqual(desiredDegree, 0): if testDiff > 240: testDiff -= 360 elif Util.fuzzyIsEqual(desiredDegree, 360): if testDiff < 120: testDiff += 360 log.debug("testDiff == {}".format(testDiff)) if testDiff < desiredDegree: t1 = testDt else: t2 = testDt # Update the curr values. currDt = t2 currDiff = testDiff longitudesP1[-1] = testValueP1 longitudesP2[-1] = testValueP2 currErrorTd = t2 - t1 # Update our lists. steps[-1] = currDt # Store the aspect timestamp. aspectTimestamps.append(currDt) elif prevDiff > desiredDegree and currDiff <= desiredDegree: log.debug("Crossed over {} from above to below!".\ format(desiredDegree)) # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than # the threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. timeWindowTd = t2 - t1 halfTimeWindowTd = \ datetime.\ timedelta(days=(timeWindowTd.days / 2.0), seconds=(timeWindowTd.seconds / 2.0), microseconds=\ (timeWindowTd.microseconds / 2.0)) testDt = t1 + halfTimeWindowTd testValueP1 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet1ParamsList, fieldName)) testValueP2 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet2ParamsList, fieldName)) log.debug("testValueP1 == {}".format(testValueP1)) log.debug("testValueP2 == {}".format(testValueP2)) if longitudesP1[-2] > 240 and testValueP1 < 120: # Planet 1 hopped over 0 degrees. testValueP1 += 360 elif longitudesP1[-2] < 120 and testValueP1 > 240: # Planet 1 hopped over 0 degrees. testValueP1 -= 360 if longitudesP2[-2] > 240 and testValueP2 < 120: # Planet 2 hopped over 0 degrees. testValueP2 += 360 elif longitudesP2[-2] < 120 and testValueP2 > 240: # Planet 2 hopped over 0 degrees. testValueP2 -= 360 testDiff = Util.toNormalizedAngle(\ testValueP1 - testValueP2) # Handle special cases of degrees 0 and 360. # Here we adjust testDiff so that it is in the # expected ranges. if Util.fuzzyIsEqual(desiredDegree, 0): if testDiff > 240: testDiff -= 360 elif Util.fuzzyIsEqual(desiredDegree, 360): if testDiff < 120: testDiff += 360 log.debug("testDiff == {}".format(testDiff)) if testDiff > desiredDegree: t1 = testDt else: t2 = testDt # Update the curr values. currDt = t2 currDiff = testDiff longitudesP1[-1] = testValueP1 longitudesP2[-1] = testValueP2 currErrorTd = t2 - t1 # Update our lists. steps[-1] = currDt # Store the aspect timestamp. aspectTimestamps.append(currDt) # Prepare for the next iteration. log.debug("steps[-1] is: {}".\ format(Ephemeris.datetimeToStr(steps[-1]))) log.debug("stepSizeTd is: {}".format(stepSizeTd)) steps.append(copy.deepcopy(steps[-1]) + stepSizeTd) del steps[0] longitudesP1.append(None) del longitudesP1[0] longitudesP2.append(None) del longitudesP2[0] # Update prevDiff as the currDiff. prevDiff = Util.toNormalizedAngle(currDiff) log.info("Number of timestamps obtained: {}".\ format(len(aspectTimestamps))) log.debug("Exiting " + inspect.stack()[0][3] + "()") return aspectTimestamps
def main(): """ """ #global highPrice #global lowPrice # Return value. rv = 0 # Check global variable inputs. if moddedHitValue >= modulusAmt: log.error("Global input variable 'moddedHitValue' " + \ "cannot be greater than the value for 'modulusAmt'.") # Don't save pccd. rv = 1 return rv if startDt > endDt: log.error("Global input variable 'startDt' cannot be after 'endDt'.") # Don't save pccd. rv = 1 return rv # Ephemeris earliest timestamp. ephemerisEarliestTimestamp = None # Ephemeris latest timestamp. ephemerisLatestTimestamp = None # Cycle hit timestamps. cycleHitTimestamps = [] # Previous and current values held across iterations of the lines # of the CSV file. prevDt = None currDt = None prevUnmoddedValue = None currUnmoddedValue = None # Counter for the current line number. lineNum = 0 # Column name. This is obtained from the first line in the CSV file. columnName = "" try: with open(inputCsvFilename) as f: for line in f: line = line.strip() #log.debug("Looking at line [{}]: {}".format(lineNum, line)) # First line has the column headers. if lineNum == 0: line = line.strip() fieldValues = line.split(delimiter) # Make sure we have enough columns of data. if columnNumber >= len(fieldValues): log.error("The input CSV file does not have enough " + \ "columns. Could not read column {}".\ format(columnNumber)) # Don't save pcdd. rv = 1 return rv # Get the column name. columnName = fieldValues[columnNumber] if lineNum >= linesToSkip: line = line.strip() fieldValues = line.split(delimiter) # Make sure we have enough columns of data. if columnNumber >= len(fieldValues): log.error("The input CSV file does not have enough " + \ "columns. Could not read column {}".\ format(columnNumber)) # Don't save pcdd. rv = 1 return rv # Get the date from this line of text and convert # to a datetime. timestampStr = fieldValues[0] currDt = convertTimestampStrToDatetime(timestampStr) # If conversion failed then don't save. if currDt == None: # Don't save pcdd. rv = 1 return rv # Store the earliest and latest timestamps of the # Ephemeris from the CSV file, as read so far. if ephemerisEarliestTimestamp == None: ephemerisEarliestTimestamp = currDt elif currDt < ephemerisEarliestTimestamp: ephemerisEarliestTimestamp = currDt if ephemerisLatestTimestamp == None: ephemerisLatestTimestamp = currDt elif currDt > ephemerisLatestTimestamp: ephemerisLatestTimestamp = currDt # Continue only if the currDt is between the start and # end timestamps. if startDt < currDt < endDt: currUnmoddedValue = float(fieldValues[columnNumber]) currModdedValue = currUnmoddedValue % modulusAmt if prevDt != None and prevUnmoddedValue != None: # Assuming heliocentric cycles, so values change # in only one direction. prevModdedValue = prevUnmoddedValue % modulusAmt if prevUnmoddedValue < currUnmoddedValue: # Increasing values. if prevModdedValue < currModdedValue: # Curr value has not looped over the # modulus amount. This is the normal case. if prevModdedValue < moddedHitValue and \ moddedHitValue <= currModdedValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. prevDiff = abs(moddedHitValue - \ prevModdedValue) currDiff = abs(currModdedValue - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) elif prevModdedValue > currModdedValue: # Curr value has looped over the modulus # amount. We must make an adjustment when # doing calculations. if prevModdedValue < moddedHitValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. prevDiff = abs(moddedHitValue - \ prevModdedValue) currDiff = abs(currModdedValue + \ modulusAmt - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) elif moddedHitValue <= currModdedValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. prevDiff = abs(moddedHitValue + \ modulusAmt - \ prevModdedValue) currDiff = abs(currModdedValue - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) elif prevUnmoddedValue > currUnmoddedValue: # Decreasing values. if currModdedValue < prevModdedValue: # Curr value has not looped over 0. # This is the normal case. if currModdedValue <= moddedHitValue and \ moddedHitValue < prevModdedValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. currDiff = abs(moddedHitValue - \ currModdedValue) prevDiff = abs(prevModdedValue - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) elif currModdedValue > prevModdedValue: # Curr value has looped over 0. # We must make an adjustment when # doing calculations. if currModdedValue <= moddedHitValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. currDiff = abs(moddedHitValue - \ currModdedValue) prevDiff = abs(prevModdedValue + \ modulusAmt - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) elif moddedHitValue < prevModdedValue: # We crossed over the moddedHitValue. # Check to see whether the # previous or the current is # closer to the moddedHitValue. currDiff = abs(moddedHitValue + \ modulusAmt - \ currModdedValue) prevDiff = abs(prevModdedValue - \ moddedHitValue) if prevDiff < currDiff: # Prev is closer. cycleHitTimestamps.append(prevDt) else: # Curr is closer. cycleHitTimestamps.append(currDt) else: # Value is the same. This is an error. log.error("Found two values in column {} ".\ format(columnNumber) + \ "that are the same value. " + \ "See line {} in file '{}'".\ format(lineNum, inputCsvFilename)) # Update variables for reading the next line. prevDt = currDt currDt = None prevUnmoddedValue = currUnmoddedValue currUnmoddedValue = None lineNum += 1 except IOError as e: log.error("Please check to make sure input CSV file '{}'".\ format(inputCsvFilename) + \ " is a file and exists.") # Don't save pcdd. rv = 1 return rv # We now have all the cycle hit timestamps that are in the time # range desired. All these timestamps should be in the list # 'cycleHitTimestamps'. # Replace spaces in the column name with underscores. columnName = columnName.replace(" ", "_") # Set the tag name that will be used for the vertical lines. tag = "{}_Mod_{}_HitTo_{}".\ format(columnName, modulusAmt, moddedHitValue) # Newline. endl = "\n" # Write results to a CSV file. outputFileLines = [] # Add column headers. outputFileLines.append("Cycle hit dates for: " + tag + endl) for dt in cycleHitTimestamps: dateStr = formatToDateStr(dt) outputFileLines.append(dateStr + endl) # Write to file. with open(outputCsvFilename, "w", encoding="utf-8") as f: for line in outputFileLines: f.write(line) log.info("Finished writing the cycle hit dates to CSV file.") # Add the vertical lines at these timestamps. #for dt in cycleHitTimestamps: # PlanetaryCombinationsLibrary.\ # addVerticalLine(pcdd, dt, # highPrice, lowPrice, tag, color) # Calculate the minimum, maximum and average length of time # between the cycle hit timestamps. prevDt = None currDt = None minimumDiffTd = None maximumDiffTd = None averageDiffTd = None totalDiffTd = datetime.timedelta(0) for i in range(len(cycleHitTimestamps)): currDt = cycleHitTimestamps[i] # We skip calculating the timedelta for the first point # because we need two timestamps to calcate the timedelta. if i != 0 and prevDt != None: diffTd = currDt - prevDt # Update values if a new minimum or maximum is seen. if minimumDiffTd == None: minimumDiffTd = diffTd elif diffTd < minimumDiffTd: minimumDiffTd = diffTd if maximumDiffTd == None: maximumDiffTd = diffTd elif diffTd > maximumDiffTd: maximumDiffTd = diffTd # Add the diffTd to the total for the average calculation later. totalDiffTd += diffTd # Update for next iteration. prevDt = currDt currDt = None # Calculate the average. if len(cycleHitTimestamps) > 1: # Convert the timedelta to seconds. totalDiffSecs = \ (totalDiffTd.microseconds + \ (totalDiffTd.seconds + (totalDiffTd.days * 24 * 3600)) * 10**6) \ / 10**6 # Compute the average. averageDiffSec = totalDiffSecs / (len(cycleHitTimestamps) - 1) log.debug("totalDiffSecs == {}".format(totalDiffSecs)) log.debug("averageDiffSec == {}".format(averageDiffSec)) # Turn the average number of seconds to a timedelta. averageDiffTd = datetime.timedelta(seconds=averageDiffSec) # Print information about parameters and cycle hit timestamps that # were found. log.info("----------------------------------------------------") log.info("Ephemeris CSV filename: '{}'".format(inputCsvFilename)) log.info("Ephemeris CSV data column number: {}".format(columnNumber)) log.info("Ephemeris earliest timestamp: {}".\ format(ephemerisEarliestTimestamp)) log.info("Ephemeris latest timestamp: {}".\ format(ephemerisLatestTimestamp)) log.info("Modulus amount: {}".format(modulusAmt)) log.info("Modded hit value: {}".format(moddedHitValue)) log.info("startDt parameter: {}".format(startDt)) log.info("endDt parameter: {}".format(endDt)) #log.info("modifyPcddFlag: {}".format(modifyPcddFlag)) #log.info("highPrice: {}".format(highPrice)) #log.info("lowPrice: {}".format(lowPrice)) log.info("Number of cycle hit points: {}".format(len(cycleHitTimestamps))) log.info("Smallest timedelta between cycle hit points: {}".\ format(minimumDiffTd)) log.info("Largest timedelta between cycle hit points: {}".\ format(maximumDiffTd)) log.info("Average timedelta between cycle hit points: {}".\ format(averageDiffTd)) # Print the cycle turn points to debug. if printCycleTurnPointsFlag == True: log.debug("----------------------------------------------------") log.debug("Cycle hit points:") log.debug("----------------------------------------------------") for dt in cycleHitTimestamps: log.debug("{} {}".format(Ephemeris.datetimeToStr(dt), tag)) log.debug("----------------------------------------------------") if modifyPcddFlag == True: # Save changes. rv = 0 else: # Don't save changes. rv = 1 return rv
def getHeliocentricPlanetNodeInfo(planetName): """ Returns a list of tuples, each tuple containing: (planetName, julianDay, datetime, "N" or "S", helioTropLongitudeOfNode, helioSidLongitudeOfNode) """ # Return value. rv = [] prevDt = None currDt = copy.deepcopy(startDt) prevLatitude = None currLatitude = None currTropLongitude = None currSidLongitude = None while currDt <= endDt: dt = currDt pi = Ephemeris.getPlanetaryInfo(planetName, dt) log.debug("Just obtained planetaryInfo for planet '{}', timestamp: {}".\ format(planetName, Ephemeris.datetimeToStr(dt))) # Get the heliocentric latitude and longitude. latitude = pi.heliocentric['tropical']['latitude'] tropLongitude = pi.heliocentric['tropical']['longitude'] sidLongitude = pi.heliocentric['sidereal']['longitude'] # Store new current planet values. currLatitude = latitude currTropLongitude = tropLongitude currSidLongitude = sidLongitude log.debug("prevLatitude={}, currLatitude={}".\ format(prevLatitude, currLatitude)) # We need two data points to proceed. if prevLatitude != None and currLatitude != None and prevDt != None: # Check to see if we passed over 0 degrees. if prevLatitude < 0.0 and currLatitude >= 0.0: # Crossed over from negative to positive! log.debug("Crossed over from negative to positive!") # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than the # desired threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. diffTd = t2 - t1 halfDiffTd = \ datetime.\ timedelta(days=(diffTd.days / 2.0), seconds=(diffTd.seconds / 2.0), microseconds=(diffTd.microseconds / 2.0)) testDt = t1 + halfDiffTd pi = Ephemeris.getPlanetaryInfo(planetName, testDt) testLatitude = pi.heliocentric['tropical']['latitude'] testTropLongitude = pi.heliocentric['tropical'][ 'longitude'] testSidLongitude = pi.heliocentric['sidereal']['longitude'] if testLatitude >= 0.0: t2 = testDt # Update the curr values as the later boundary. currDt = t2 currLatitude = testLatitude currTropLongitude = testTropLongitude currSidLongitude = testSidLongitude else: t1 = testDt currErrorTd = t2 - t1 # Broke out of while loop, meaning we have a timestamp # within our threshold. # Create a tuple to add to our list. tup = (planetName, Ephemeris.datetimeToJulianDay(currDt), currDt, northStr, currTropLongitude, currSidLongitude) # Append to the list. rv.append(tup) elif prevLatitude > 0.0 and currLatitude <= 0.0: # Crossed over from positive to negative! log.debug("Crossed over from positive to negative!") # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than the # desired threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. diffTd = t2 - t1 halfDiffTd = \ datetime.\ timedelta(days=(diffTd.days / 2.0), seconds=(diffTd.seconds / 2.0), microseconds=(diffTd.microseconds / 2.0)) testDt = t1 + halfDiffTd pi = Ephemeris.getPlanetaryInfo(planetName, testDt) testLatitude = pi.heliocentric['tropical']['latitude'] testTropLongitude = pi.heliocentric['tropical'][ 'longitude'] testSidLongitude = pi.heliocentric['sidereal']['longitude'] if testLatitude <= 0.0: t2 = testDt # Update the curr values as the later boundary. currDt = t2 currLatitude = testLatitude currTropLongitude = testTropLongitude currSidLongitude = testSidLongitude else: t1 = testDt currErrorTd = t2 - t1 # Broke out of while loop, meaning we have a timestamp # within our threshold. # Create a tuple to add to our list. tup = (planetName, Ephemeris.datetimeToJulianDay(currDt), currDt, southStr, currTropLongitude, currSidLongitude) # Append to the list. rv.append(tup) # Increment currDt timestamp. prevDt = currDt currDt = currDt + stepSizeTd # Move the previous currLatitude to prevLatitude. prevLatitude = currLatitude currLatitude = None currTropLongitude = None currSidLongitude = None log.debug("prevLatitude={}, currLatitude={}".\ format(prevLatitude, currLatitude)) return rv
def getOnePlanetLongitudeAspectTimestamps(\ startDt, endDt, planet1Params, fixedDegree, degreeDifference, uniDirectionalAspectsFlag=False, maxErrorTd=datetime.timedelta(hours=1)): """Obtains a list of datetime.datetime objects that contain the moments when the aspect specified is active. The aspect is measured by formula: (planet longitude) - (fixed longitude degree) Arguments: startDt - datetime.datetime object for the starting timestamp to do the calculations for artifacts. endDt - datetime.datetime object for the ending timestamp to do the calculations for artifacts. highPrice - float value for the high price to end the vertical line. lowPrice - float value for the low price to end the vertical line. planet1Params - Tuple containing: (planetName, centricityType, longitudeType) Where: planetName - str holding the name of the second planet to do the calculations for. centricityType - str value holding either "geocentric", "topocentric", or "heliocentric". longitudeType - str value holding either "tropical" or "sidereal". fixedDegree - float holding the fixed degree in the zodiac circle. degreeDifference - float value for the number of degrees of separation for this aspect. uniDirectionalAspectsFlag - bool value for whether or not uni-directional aspects are enabled or not. By default, aspects are bi-directional, so Saturn square-aspect Jupiter would be the same as Jupiter square-aspect Saturn. If this flag is set to True, then those two combinations would be considered unique. In the case where the flag is set to True, for the aspect to be active, planet2 would need to be 'degreeDifference' degrees in front of planet1. maxErrorTd - datetime.timedelta object holding the maximum time difference between the exact planetary combination timestamp, and the one calculated. This would define the accuracy of the calculations. Returns: List of datetime.datetime objects. Each timestamp in the list is the moment where the aspect is active and satisfies the given parameters. In the event of an error, the reference None is returned. """ log.debug("Entered " + inspect.stack()[0][3] + "()") # List of timestamps of the aspects found. aspectTimestamps = [] # Make sure the inputs are valid. if endDt < startDt: log.error("Invalid input: 'endDt' must be after 'startDt'") return None # Check to make sure planet params were given. if len(planet1Params) != 3: log.error("planet1Params must be a tuple with 3 elements.") return None if not isinstance(fixedDegree, (int, float, complex)): log.error("fixedDegree must be a number.") return None # Normalize the fixed degree. fixedDegree = Util.toNormalizedAngle(fixedDegree) log.debug("planet1Params passed in is: {}".\ format(planet1Params)) log.debug("fixedDegree passed in is: {}".\ format(fixedDegree)) # Check inputs of planet parameters. planetName = planet1Params[0] centricityType = planet1Params[1] longitudeType = planet1Params[2] # Check inputs for centricity type. loweredCentricityType = centricityType.lower() if loweredCentricityType != "geocentric" and \ loweredCentricityType != "topocentric" and \ loweredCentricityType != "heliocentric": log.error("Invalid input: Centricity type is invalid. " + \ "Value given was: {}".format(centricityType)) return None # Check inputs for longitude type. loweredLongitudeType = longitudeType.lower() if loweredLongitudeType != "tropical" and \ loweredLongitudeType != "sidereal": log.error("Invalid input: Longitude type is invalid. " + \ "Value given was: {}".format(longitudeType)) return None # Field name we are getting. fieldName = "longitude" # Initialize the Ephemeris with the birth location. log.debug("Setting ephemeris location ...") Ephemeris.setGeographicPosition(locationLongitude, locationLatitude, locationElevation) # Set the step size. stepSizeTd = datetime.timedelta(days=1) planetName = planet1Params[0] if Ephemeris.isHouseCuspPlanetName(planetName) or \ Ephemeris.isAscmcPlanetName(planetName): # House cusps and ascmc planets need a smaller step size. stepSizeTd = datetime.timedelta(hours=1) elif planetName == "Moon": # Use a smaller step size for the moon so we can catch # smaller aspect sizes. stepSizeTd = datetime.timedelta(hours=3) log.debug("Step size is: {}".format(stepSizeTd)) # Desired angles. We need to check for planets at these angles. desiredAngleDegList = [] desiredAngleDeg1 = Util.toNormalizedAngle(degreeDifference) desiredAngleDegList.append(desiredAngleDeg1) if Util.fuzzyIsEqual(desiredAngleDeg1, 0): desiredAngleDegList.append(360) if uniDirectionalAspectsFlag == False: desiredAngleDeg2 = \ 360 - Util.toNormalizedAngle(degreeDifference) if desiredAngleDeg2 not in desiredAngleDegList: desiredAngleDegList.append(desiredAngleDeg2) # Debug output. anglesStr = "" for angle in desiredAngleDegList: anglesStr += "{} ".format(angle) log.debug("Angles in desiredAngleDegList: " + anglesStr) # Iterate through, appending to aspectTimestamps list as we go. steps = [] steps.append(copy.deepcopy(startDt)) steps.append(copy.deepcopy(startDt)) longitudesP1 = [] longitudesP1.append(None) longitudesP1.append(None) longitudesP2 = [] longitudesP2.append(None) longitudesP2.append(None) def getFieldValue(dt, planetParams, fieldName): """Creates the PlanetaryInfo object for the given planetParamsList and returns the value of the field desired. """ log.debug("planetParams passed in is: {}".\ format(planetParams)) t = planetParams planetName = t[0] centricityType = t[1] longitudeType = t[2] pi = Ephemeris.getPlanetaryInfo(planetName, dt) log.debug("Planet {} has geo sid longitude: {}".\ format(planetName, pi.geocentric["sidereal"]["longitude"])) fieldValue = None if centricityType.lower() == "geocentric": fieldValue = pi.geocentric[longitudeType][fieldName] elif centricityType.lower() == "topocentric": fieldValue = pi.topocentric[longitudeType][fieldName] elif centricityType.lower() == "heliocentric": fieldValue = pi.heliocentric[longitudeType][fieldName] else: log.error("Unknown centricity type: {}".\ format(centricityType)) fieldValue = None return fieldValue log.debug("Stepping through timestamps from {} to {} ...".\ format(Ephemeris.datetimeToStr(startDt), Ephemeris.datetimeToStr(endDt))) currDiff = None prevDiff = None while steps[-1] < endDt: currDt = steps[-1] prevDt = steps[-2] log.debug("Looking at currDt == {} ...".\ format(Ephemeris.datetimeToStr(currDt))) longitudesP1[-1] = \ Util.toNormalizedAngle(\ getFieldValue(currDt, planet1Params, fieldName)) longitudesP2[-1] = fixedDegree log.debug("{} {} is: {}".\ format(planet1Params, fieldName, longitudesP1[-1])) log.debug("fixedDegree is: {}".format(longitudesP2[-1])) currDiff = Util.toNormalizedAngle(\ longitudesP1[-1] - longitudesP2[-1]) log.debug("prevDiff == {}".format(prevDiff)) log.debug("currDiff == {}".format(currDiff)) if prevDiff != None and \ longitudesP1[-2] != None and \ longitudesP2[-2] != None: if abs(prevDiff - currDiff) > 180: # Probably crossed over 0. Adjust the prevDiff so # that the rest of the algorithm can continue to # work. if prevDiff > currDiff: prevDiff -= 360 else: prevDiff += 360 log.debug("After adjustment: prevDiff == {}".\ format(prevDiff)) log.debug("After adjustment: currDiff == {}".\ format(currDiff)) for desiredAngleDeg in desiredAngleDegList: log.debug("Looking at desiredAngleDeg: {}".\ format(desiredAngleDeg)) desiredDegree = desiredAngleDeg if prevDiff < desiredDegree and currDiff >= desiredDegree: log.debug("Crossed over {} from below to above!".\ format(desiredDegree)) # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than # the threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. timeWindowTd = t2 - t1 halfTimeWindowTd = \ datetime.\ timedelta(days=(timeWindowTd.days / 2.0), seconds=(timeWindowTd.seconds / 2.0), microseconds=\ (timeWindowTd.microseconds / 2.0)) testDt = t1 + halfTimeWindowTd testValueP1 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet1Params, fieldName)) testValueP2 = \ Util.toNormalizedAngle(fixedDegree) log.debug("testValueP1 == {}".format(testValueP1)) log.debug("testValueP2 == {}".format(testValueP2)) if longitudesP1[-2] > 240 and testValueP1 < 120: # Planet 1 hopped over 0 degrees. testValueP1 += 360 elif longitudesP1[-2] < 120 and testValueP1 > 240: # Planet 1 hopped over 0 degrees. testValueP1 -= 360 if longitudesP2[-2] > 240 and testValueP2 < 120: # Planet 2 hopped over 0 degrees. testValueP2 += 360 elif longitudesP2[-2] < 120 and testValueP2 > 240: # Planet 2 hopped over 0 degrees. testValueP2 -= 360 testDiff = Util.toNormalizedAngle(\ testValueP1 - testValueP2) # Handle special cases of degrees 0 and 360. # Here we adjust testDiff so that it is in the # expected ranges. if Util.fuzzyIsEqual(desiredDegree, 0): if testDiff > 240: testDiff -= 360 elif Util.fuzzyIsEqual(desiredDegree, 360): if testDiff < 120: testDiff += 360 log.debug("testDiff == {}".format(testDiff)) if testDiff < desiredDegree: t1 = testDt else: t2 = testDt # Update the curr values. currDt = t2 currDiff = testDiff longitudesP1[-1] = testValueP1 longitudesP2[-1] = testValueP2 currErrorTd = t2 - t1 # Update our lists. steps[-1] = currDt # Store the aspect timestamp. aspectTimestamps.append(currDt) elif prevDiff > desiredDegree and currDiff <= desiredDegree: log.debug("Crossed over {} from above to below!".\ format(desiredDegree)) # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than # the threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. timeWindowTd = t2 - t1 halfTimeWindowTd = \ datetime.\ timedelta(days=(timeWindowTd.days / 2.0), seconds=(timeWindowTd.seconds / 2.0), microseconds=\ (timeWindowTd.microseconds / 2.0)) testDt = t1 + halfTimeWindowTd testValueP1 = \ Util.toNormalizedAngle(getFieldValue(\ testDt, planet1ParamsList, fieldName)) testValueP2 = \ Util.toNormalizedAngle(fixedDegree) log.debug("testValueP1 == {}".format(testValueP1)) log.debug("testValueP2 == {}".format(testValueP2)) if longitudesP1[-2] > 240 and testValueP1 < 120: # Planet 1 hopped over 0 degrees. testValueP1 += 360 elif longitudesP1[-2] < 120 and testValueP1 > 240: # Planet 1 hopped over 0 degrees. testValueP1 -= 360 if longitudesP2[-2] > 240 and testValueP2 < 120: # Planet 2 hopped over 0 degrees. testValueP2 += 360 elif longitudesP2[-2] < 120 and testValueP2 > 240: # Planet 2 hopped over 0 degrees. testValueP2 -= 360 testDiff = Util.toNormalizedAngle(\ testValueP1 - testValueP2) # Handle special cases of degrees 0 and 360. # Here we adjust testDiff so that it is in the # expected ranges. if Util.fuzzyIsEqual(desiredDegree, 0): if testDiff > 240: testDiff -= 360 elif Util.fuzzyIsEqual(desiredDegree, 360): if testDiff < 120: testDiff += 360 log.debug("testDiff == {}".format(testDiff)) if testDiff > desiredDegree: t1 = testDt else: t2 = testDt # Update the curr values. currDt = t2 currDiff = testDiff longitudesP1[-1] = testValueP1 longitudesP2[-1] = testValueP2 currErrorTd = t2 - t1 # Update our lists. steps[-1] = currDt # Store the aspect timestamp. aspectTimestamps.append(currDt) # Prepare for the next iteration. log.debug("steps[-1] is: {}".\ format(Ephemeris.datetimeToStr(steps[-1]))) log.debug("stepSizeTd is: {}".format(stepSizeTd)) steps.append(copy.deepcopy(steps[-1]) + stepSizeTd) del steps[0] longitudesP1.append(None) del longitudesP1[0] longitudesP2.append(None) del longitudesP2[0] # Update prevDiff as the currDiff. prevDiff = Util.toNormalizedAngle(currDiff) log.debug("Number of timestamps obtained: {}".\ format(len(aspectTimestamps))) log.debug("Exiting " + inspect.stack()[0][3] + "()") return aspectTimestamps
def processPCDD(pcdd, tag): """ Module for printing information about the BirthInfo in a PriceChartDocumentData object. BirthInfo printed includes: - Birth location name. - Birth country name. - Birth location coordinates. - Birth timestamp as a UTC datetime.datetime - Birth timestamp as a julian day. Arguments: pcdd - PriceChartDocumentData object that will be modified. tag - str containing the tag. The value of this field may be "" if a tag is not specified by the user. This implementation doesn't use this field. Returns: 0 if the changes are to be saved to file. 1 if the changes are NOT to be saved to file. This implementation always returns 1. """ # Return value. rv = 1 birthInfo = pcdd.birthInfo # Convert longitude from a float value to degrees, # minutes, seconds and East/West polarity. (lonDegrees, lonMinutes, lonSeconds, lonPolarity) = \ GeoInfo.longitudeToDegMinSec(birthInfo.longitudeDegrees) # Convert latitude from a float value to degrees, minutes, # seconds and North/South polarity. (latDegrees, latMinutes, latSeconds, latPolarity) = \ GeoInfo.latitudeToDegMinSec(birthInfo.latitudeDegrees) log.info("") log.info("Birth location name: {}".format(birthInfo.locationName)) log.info("Birth location country: {}".format(birthInfo.countryName)) log.info("Birth location longitude: {} {} {}' {} ({})".\ format(lonDegrees, lonPolarity, lonMinutes, lonSeconds, birthInfo.longitudeDegrees)) log.info("Birth location latitude: {} {} {}' {} ({})".\ format(latDegrees, latPolarity, latMinutes, latSeconds, birthInfo.latitudeDegrees)) birthLocalizedDatetime = birthInfo.getBirthLocalizedDatetime() birthUtcDatetime = birthInfo.getBirthUtcDatetime() birthJd = Ephemeris.datetimeToJulianDay(birthUtcDatetime) log.info("Birth timestamp (localized): {}".\ format(Ephemeris.datetimeToStr(birthLocalizedDatetime))) log.info("Birth timestamp (UTC): {}".\ format(Ephemeris.datetimeToStr(birthUtcDatetime))) log.info("Birth timestamp (julian day): {}".\ format(birthJd)) log.info("") rv = 1 return rv
def processSwingFileData(swingFileData): """Processes the given SwingFileData. What this means in detail is it will scan through the PriceBars in the given SwingFileData, looking for relative highs and lows within our parameters for a window size, and if the PriceBar is determined to be a high or low, then the PriceBar will be tagged appropriately. When this function is complete, the SwingFileData will only contain PriceBars that are tagged and deemed to be a high or low. This function will also set the field 'swingFileDescription' within the given SwingFileData object with information about the parameters used to filter out the PriceBar highs and lows. """ # TODO: Add a flag and the code needed to implement filtering out highs and lows if they are not sharp enough. (i.e. Filter out roving turns). # Parameters used to process the swing file: scanVars = [] # Dictionaries below have the following keys-value fields: # # "tag": Text string for the tag. The character 'H' starting the # string indicates a high is being seeked. The character 'L' # starting the string indicates a low is being seeked. # # "priceRangeRequired": Percentage required for the range of # PriceBar prices in the scanning window to have in order for # the PriceBar to be able to be tagged as a high or low. # These are percentages with values in the range [0, 1]. # # "param": The parameter value for 'X' in the following # explanation of how highs and lows are determined. # # A PriceBar is considered a high if the following are true: # # 1) It has X number of PriceBars with lower or equal prices to it, # coming before the timestamp of the PriceBar. # 2) It has X number of PriceBars with lower or equal prices to it. # coming after the timestamp of the PriceBar. # # In case of a tie between PriceBars that are next to each other, # the later bar wins. # # A PriceBar is considered a low if the following are true: # # 1) It has X number of PriceBars with higher or equal prices to it, # coming before the timestamp of the PriceBar. # 2) It has X number of PriceBars with higher or equal prices to it. # coming after the timestamp of the PriceBar. # # In case of a tie between PriceBars that are next to each other, # the later bar wins. # # 'window': List of PriceBars, used as the window for our # scanning. The PriceBar being evaluated would be the one in # the middle of this list. The size of the list would thus be # determined with the formula: (2 * param) + 1. # #scanVars.append(\ # { "tag" : "H", # "priceRangeRequired" : 0.05, # "param" : 5, # "window" : [] }) scanVars.append(\ { "tag" : "HH", "priceRangeRequired" : 0.10, "param" : 20, "window" : [] }) scanVars.append(\ { "tag" : "HHH", "priceRangeRequired" : 0.20, "param" : 30, "window" : [] }) scanVars.append(\ { "tag" : "HHHH", "priceRangeRequired" : 0.25, "param" : 80, "window" : [] }) scanVars.append(\ { "tag" : "HHHHH", "priceRangeRequired" : 0.30, "param" : 160, "window" : [] }) #scanVars.append(\ # { "tag" : "L", # "priceRangeRequired" : 0.05, # "param" : 5, # "window" : [] }) scanVars.append(\ { "tag" : "LL", "priceRangeRequired" : 0.10, "param" : 20, "window" : [] }) scanVars.append(\ { "tag" : "LLL", "priceRangeRequired" : 0.20, "param" : 30, "window" : [] }) scanVars.append(\ { "tag" : "LLLL", "priceRangeRequired" : 0.25, "param" : 80, "window" : [] }) scanVars.append(\ { "tag" : "LLLLL", "priceRangeRequired" : 0.30, "param" : 160, "window" : [] }) scanVarsStr = "[" for i in range(len(scanVars)): scanVar = scanVars[i] scanVarsStr += "{}".format(scanVar) if i != len(scanVars) - 1: scanVarsStr += "," scanVarsStr += "]" # Get the timestamp of the earliest PriceBar. earliestPriceBarTimestamp = None latestPriceBarTimestamp = None if len(swingFileData.priceBars) > 0: earliestPriceBarTimestamp = swingFileData.priceBars[0].timestamp latestPriceBarTimestamp = swingFileData.priceBars[-1].timestamp # Before we start doing any tagging ourselves, count how many # PriceBars are already tagged. count = 0 originalPriceBarsLen = len(swingFileData.priceBars) for pb in swingFileData.priceBars: if len(pb.tags) > 0: count += 1 pb.clearTags() if count > 0: log.warn("Before scanning, tagging, and extracting, " + \ "{} out of {} PriceBars already have tags.".\ format(count, originalPriceBarsLen)) log.info("Scanning for highs and lows among {} PriceBars ...".\ format(originalPriceBarsLen)) # Start scanning. for pb in swingFileData.priceBars: log.debug("=============================================") log.debug("Looking at PriceBar with timestamp: {}".\ format(Ephemeris.datetimeToStr(pb.timestamp))) for scanVar in scanVars: log.debug("---------------------------------------------") log.debug("Currently looking at tag: {}".format(scanVar['tag'])) # Calculate the required window size for this scanVar. requiredWindowSize = (scanVar['param'] * 2) + 1 log.debug("requiredWindowSize == {}".format(requiredWindowSize)) # Alias for the window. window = scanVar['window'] log.debug("Before appending PriceBar, len(window) == {}".\ format(len(window))) # Index into the window, pointing to the PriceBar # currently being inspected for a high or low. currIndex = scanVar['param'] log.debug("currIndex == {}".format(currIndex)) # Append the PriceBar. window.append(pb) # If the size of the window is now larger than the # required size, then shrink it appropriately. while len(window) > requiredWindowSize: # Drop the PriceBar at the beginning of the window. window = window[1:] # If the size of the window is not large enough, don't do # any checks for a high or low yet. if len(window) < requiredWindowSize: continue log.debug("For scanVar['tag'] == '{}', len(window) == {}".\ format(scanVar['tag'], len(window))) # First make sure the price range of these PriceBars is # sufficient for this scanVar. highPrice = None lowPrice = None for i in range(len(window)): if highPrice == None: highPrice = window[i].high if lowPrice == None: lowPrice = window[i].low if highPrice <= window[i].high: highPrice = window[i].high if lowPrice >= window[i].low: lowPrice = window[i].low priceRange = highPrice - lowPrice percentage = priceRange / highPrice if percentage < scanVar['priceRangeRequired']: log.debug("Price range not wide enough to warrant " + \ "investigating this PriceBar. " + \ "tag=\'{}\', percentage={}, percentageRequired={}".\ format(scanVar['tag'], percentage, scanVar['priceRangeRequired'])) # Go on to the next scanVar. continue else: log.debug("Price range is wide enough.") #log.debug("Price range is wide enough. " + \ # "tag=\'{}\', percentage={}, percentageRequired={}".\ # format(scanVar['tag'], # percentage, # scanVar['priceRangeRequired'])) pass if scanVar['tag'].startswith("L"): # We are looking for a low. currIndexIsTheLowest = True for i in range(len(window)): log.debug("i == {}, currIndex == {}".format(i, currIndex)) if i != currIndex: log.debug("window[i].low == {}".\ format(window[i].low)) log.debug("window[currIndex].low == {}".\ format(window[currIndex].low)) if window[i].low < window[currIndex].low: currIndexIsTheLowest = False log.debug("currIndexIsTheLowest == False") break if currIndexIsTheLowest == True: log.debug("Checking the next index after currIndex.") # Check the PriceBar after currIndex, to see if it # has a low equal to this one. If it does, then # currIndex is not a low for our definition. if currIndex + 1 < len(window): log.debug("window[{}].low == {}".\ format(currIndex, window[currIndex].low)) log.debug("window[{}].low == {}".\ format(currIndex+1, window[currIndex+1].low)) if window[currIndex+1].low <= window[currIndex].low: currIndexIsTheLowest = False log.debug("currIndexIsTheLowest == False") # If the PriceBar is still the lowest, then tag it. if currIndexIsTheLowest == True: log.debug("Adding tag '{}' to PriceBar with timestamp: {}".\ format(scanVar['tag'], Ephemeris.datetimeToStr(\ window[currIndex].timestamp))) window[currIndex].addTag(scanVar['tag']) log.debug("PriceBar is now: {}".\ format(window[currIndex].toString())) elif scanVar['tag'].startswith("H"): # We are looking for a high. currIndexIsTheHighest = True for i in range(len(window)): log.debug("i == {}, currIndex == {}".format(i, currIndex)) if i != currIndex: log.debug("window[i].high == {}".\ format(window[i].high)) log.debug("window[currIndex].high == {}".\ format(window[currIndex].high)) if window[i].high > window[currIndex].high: currIndexIsTheHighest = False log.debug("currIndexIsTheHighest == False") break if currIndexIsTheHighest == True: log.debug("Checking the next index after currIndex.") # Check the PriceBar after currIndex, to see if it # has a high equal to this one. If it does, then # currIndex is not a high for our definition. if currIndex + 1 < len(window): log.debug("window[{}].high == {}".\ format(currIndex, window[currIndex].high)) log.debug("window[{}].high == {}".\ format(currIndex+1, window[currIndex+1].high)) if window[currIndex+1].high >= window[currIndex].high: currIndexIsTheHighest = False log.debug("currIndexIsTheHighest == False") # If the PriceBar is still the highest, then tag it. if currIndexIsTheHighest == True: log.debug("Adding tag '{}' to PriceBar with timestamp: {}".\ format(scanVar['tag'], Ephemeris.datetimeToStr(\ window[currIndex].timestamp))) window[currIndex].addTag(scanVar['tag']) log.debug("PriceBar is now: {}".\ format(window[currIndex].toString())) else: log.warn("Warning, this tag name is not supported.") log.debug("Done scanning.") # Grab all the PriceBars with tags. taggedPriceBars = [] for pb in swingFileData.priceBars: if len(pb.tags) > 0: debugStr = "PriceBar at {} has the following tags: ".\ format(Ephemeris.datetimeToStr(pb.timestamp)) debugStr += "[" for i in range(len(pb.tags)): debugStr += pb.tags[i] if i != len(pb.tags) - 1: debugStr += "," debugStr += "]" log.debug(debugStr) taggedPriceBars.append(pb) log.info("Scanning for highs and lows complete.") log.info("Out of {} PriceBars, {} were extracted and tagged.".\ format(originalPriceBarsLen, len(taggedPriceBars))) # Replace swingFileData.priceBars with the list with only tagged PriceBars. swingFileData.priceBars = taggedPriceBars # Set the string holding the parameters that were used to scan and # filter for highs and lows. swingFileDescriptionText = \ "Swing file creation timestamp: {}. ".\ format(datetime.datetime.now(tz=pytz.utc)) + \ "The swings included in this file were extracted from " + \ "{} PriceBars, starting from {} to {}. ".\ format(originalPriceBarsLen, Ephemeris.datetimeToStr(earliestPriceBarTimestamp), Ephemeris.datetimeToStr(latestPriceBarTimestamp)) + \ "Total number of PriceBars tagged as highs and lows " + \ "in this swing file is: {}. ".\ format(len(swingFileData.priceBars)) + \ "The parameters used in scanning and tagging were: " + \ scanVarsStr swingFileData.swingFileDescription = swingFileDescriptionText log.debug("swingFileData.swingFileDescription == {}".\ format(swingFileData.swingFileDescription)) log.debug("Done processing swing file data.")
def getEphemerisDataLineForDatetime(dt): """Obtains the line of CSV text of planetary position data. Arguments: dt - datetime.datetime object with the timestamp seeked. Returns: str in CSV format. Since there are a lot of fields, please See the section of code where we write the header info str for the format. """ # Return value. rv = "" planetaryInfos = getPlanetaryInfosForDatetime(dt) log.debug("Just obtained planetaryInfos for timestamp: {}".\ format(Ephemeris.datetimeToStr(dt))) # Planet geocentric longitude 15-degree axis points. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] rv += "{:.3f},".format(lon % 15.0) # Planet geocentric longitude. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] rv += "{:.3f},".format(lon) # Planet geocentric longitude in zodiac str format. for planetName in geocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.geocentric['tropical']['longitude'] valueStr = \ AstrologyUtils.\ convertLongitudeToStrWithRasiAbbrev(lon) rv += valueStr + "," # Planet heliocentric longitude 15-degree axis points. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] rv += "{:.3f},".format(lon % 15.0) # Planet heliocentric longitude. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] rv += "{:.3f},".format(lon) # Planet heliocentric longitude in zodiac str format. for planetName in heliocentricPlanetNames: for pi in planetaryInfos: if pi.name == planetName: lon = pi.heliocentric['tropical']['longitude'] valueStr = \ AstrologyUtils.\ convertLongitudeToStrWithRasiAbbrev(lon) rv += valueStr + "," # Planet declination. for planetName in declinationPlanetNames: for pi in planetaryInfos: if pi.name == planetName: declination = pi.geocentric['tropical']['declination'] rv += "{:.3f},".format(declination) # Planet geocentric latitude. for planetName in geocentricLatitudePlanetNames: for pi in planetaryInfos: if pi.name == planetName: latitude = pi.geocentric['tropical']['latitude'] rv += "{:.3f},".format(latitude) # Planet heliocentric latitude. for planetName in heliocentricLatitudePlanetNames: for pi in planetaryInfos: if pi.name == planetName: latitude = pi.heliocentric['tropical']['latitude'] rv += "{:.3f},".format(latitude) # Remove trailing comma. rv = rv[:-1] return rv
#if __name__ == "__main__": # Initialize Ephemeris (required). Ephemeris.initialize() # Set the Location (required). Ephemeris.setGeographicPosition(locationLongitude, locationLatitude, locationElevation) # Log the parameters that are being used. log.info("Location used is: {} (lat={}, lon={})".\ format(locationName, locationLatitude, locationLongitude)) log.info("Timezone used is: {}".format(timezone.zone)) log.info("Start timestamp: {}".format(Ephemeris.datetimeToStr(startDt))) log.info("End timestamp: {}".format(Ephemeris.datetimeToStr(endDt))) # Compile the header line text. headerLine = "" headerLine += "Date" + "," headerLine += "Day of week" + "," headerLine += "Day count" + "," headerLine += "Week count" + "," headerLine += "Month count" + "," # Planet geocentric longitude mod 15. for planetName in geocentricPlanetNames: headerLine += "G." + planetName + "%15" + "," # Planet geocentric longitude.
for planetName in geocentricPlanetNames: listOfTuplesForPlanet = results[planetName] for tup in listOfTuplesForPlanet: planetName = tup[0] jd = tup[1] dt = tup[2] retroOrDirect = tup[3] geoTropLongitudeOfPlanet = tup[4] geoSidLongitudeOfPlanet = tup[5] # Assemble the line that will go into the CSV file. line = "" line += "{}".format(planetName) + "," line += "{}".format(jd) + "," line += "{}".format(Ephemeris.datetimeToStr(dt)) + "," line += "{}".format(retroOrDirect) + "," line += "{}".format(geoTropLongitudeOfPlanet) + "," line += "{}".format(geoSidLongitudeOfPlanet) + "," # Remove last trailing comma. line = line[:-1] # Append to the output lines. outputLines.append(line) # Write outputLines to output file. with open(outputFilename, "w", encoding="utf-8") as f: log.info("Writing to output file '{}' ...".format(outputFilename))
def getLongitudeDiffBetweenDatetimes(planetName, centricityType, dt1, loc1Tuple, dt2, loc2Tuple): startTimestamp = dt1 endTimestamp = dt2 loc1Name = loc1Tuple[0] loc1Longitude = loc1Tuple[1] loc1Latitude = loc1Tuple[2] loc1Elevation = loc1Tuple[3] loc2Name = loc2Tuple[0] loc2Longitude = loc2Tuple[1] loc2Latitude = loc2Tuple[2] loc2Elevation = loc2Tuple[3] # maxErrorTd - datetime.timedelta object holding the maximum # time difference between the exact planetary # timestamp for the phenomena, and the one # calculated. This would define the accuracy of # the calculations. # maxErrorTd = datetime.timedelta(seconds=4) # Size of a circle, in degrees. # # Here we define our own value instead of using the value in # AstrologyUtils.degreesInCircle because it is possible we may # want to test different sizes of a 'circle'. circleSizeInDegrees = 360.0 # All references to longitude_speed need to # be from tropical zodiac measurements! If I use # sidereal zodiac measurements for getting the # longitude_speed, then the measurements from the # Swiss Ephemeris do not yield the correct values. # I use the following variable in these locations. zodiacTypeForLongitudeSpeed = "tropical" tropicalZodiacFlag = True # Text to set in the text item. text = "" # Total number of degrees elapsed. totalDegrees = 0 Ephemeris.setGeographicPosition(loc1Longitude, loc1Latitude, loc1Elevation) # List of PlanetaryInfo objects for this particular # planet, sorted by timestamp. planetData = [] # Step size to use in populating the data list with # PlanetaryInfos. # # The step size should cause the planet to move less # than 120 degrees in all cases, and idealy much less # than this, that way we can easily narrow down when # the planet passes the 0 degree or 360 degree # threshold, and also so it is easier to narrow down # when retrograde periods happen. If the step size is # too large, it is possible that we would miss a whole # time window of retrograde movement, so discretion # has to be used in determining what to use for this value. # # Here we will set it to 1 day for the default case, # but if the planet name is a house cusp then shrink # the step size so we will get the correct resolution. # Also, if the planet name is an outer planet with a # large period, we can increase the step size slightly # to improve performance. stepSizeTd = datetime.timedelta(days=1) if Ephemeris.isHouseCuspPlanetName(planetName) or \ Ephemeris.isAscmcPlanetName(planetName): stepSizeTd = datetime.timedelta(hours=1) elif planetName == "Jupiter" or \ planetName == "Saturn" or \ planetName == "Neptune" or \ planetName == "Uranus" or \ planetName == "Pluto": stepSizeTd = datetime.timedelta(days=5) log.debug("Stepping through from {} to {} ...".\ format(Ephemeris.datetimeToStr(startTimestamp), Ephemeris.datetimeToStr(endTimestamp))) # Current datetime as we step through all the # timestamps between the start and end timestamp. currDt = copy.deepcopy(startTimestamp) # Step through the timestamps, calculating the planet positions. while currDt < endTimestamp: p = Ephemeris.getPlanetaryInfo(planetName, currDt) planetData.append(p) # Increment step size. currDt += stepSizeTd # We must also append the planet calculation for the end timestamp. Ephemeris.setGeographicPosition(loc2Longitude, loc2Latitude, loc2Elevation) p = Ephemeris.getPlanetaryInfo(planetName, endTimestamp) planetData.append(p) # Geocentric measurement. if centricityType == "geocentric": # Get the PlanetaryInfos for the timestamps of the # planet at the moment right after the # longitude_speed polarity changes. additionalPlanetaryInfos = [] prevLongitudeSpeed = None for i in range(len(planetData)): currLongitudeSpeed = \ planetData[i].geocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'] if prevLongitudeSpeed != None and \ ((prevLongitudeSpeed < 0 and currLongitudeSpeed >= 0) or \ (prevLongitudeSpeed >= 0 and currLongitudeSpeed < 0)): # Polarity changed. # Try to narrow down the exact moment in # time when this occured. t1 = planetData[i-1].dt t2 = planetData[i].dt currErrorTd = t2 - t1 while currErrorTd > maxErrorTd: if log.isEnabledFor(logging.DEBUG) == True: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. diffTd = t2 - t1 halfDiffTd = \ datetime.\ timedelta(days=(diffTd.days / 2.0), seconds=(diffTd.seconds / 2.0), microseconds=(diffTd.\ microseconds / 2.0)) testDt = t1 + halfDiffTd p = Ephemeris.getPlanetaryInfo(planetName, testDt) testLongitudeSpeed = \ p.geocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'] if ((prevLongitudeSpeed < 0 and \ testLongitudeSpeed >= 0) or \ (prevLongitudeSpeed >= 0 and \ testLongitudeSpeed < 0)): # Polarity change at the test timestamp. t2 = testDt else: # No polarity change yet. t1 = testDt # Update the currErrorTd. currErrorTd = t2 - t1 log.debug("Broke out of loop to find " + \ "velocity polarity change. " + \ "currErrorTd is: {}, ".\ format(currErrorTd)) # Timestamp at t2 is now within the amount # of the time error threshold ('maxErrorTd') # following the polarity change. # Append this value to the list. p = Ephemeris.getPlanetaryInfo(planetName, t2) additionalPlanetaryInfos.append(p) t1pi = planetData[i-1] t2pi = Ephemeris.getPlanetaryInfo(planetName, t2) if log.isEnabledFor(logging.DEBUG) == True: log.debug("t1 == {}, ".\ format(Ephemeris.datetimeToStr(t1pi.dt)) + \ "longitude(tropical) == {}, ".\ format(t1pi.geocentric['tropical']['longitude']) + \ "longitude(sidereal) == {}, ".\ format(t1pi.geocentric['sidereal']['longitude']) + \ "longitude_speed == {}, ".\ format(t1pi.geocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'])) log.debug("t2 == {}, ".\ format(Ephemeris.datetimeToStr(t2pi.dt)) + \ "longitude(tropical) == {}, ".\ format(t2pi.geocentric['tropical']['longitude']) + \ "longitude(sidereal) == {}, ".\ format(t2pi.geocentric['sidereal']['longitude']) + \ "longitude_speed == {}, ".\ format(t2pi.geocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'])) # There is no need to update # currLongitudeSpeed here, because the # longitude_speed for 'p' should be the # same polarity. # Update prevLongitudeSpeed. prevLongitudeSpeed = currLongitudeSpeed # Sort all the extra PlanetaryInfo objects by timestamp. additionalPlanetaryInfos = \ sorted(additionalPlanetaryInfos, key=lambda c: c.dt) # Insert PlanetaryInfos from # 'additionalPlanetaryInfos' into 'planetData' at # the timestamp-ordered location. currLoc = 0 for i in range(len(additionalPlanetaryInfos)): pi = additionalPlanetaryInfos[i] insertedFlag = False while currLoc < len(planetData): if pi.dt < planetData[currLoc].dt: planetData.insert(currLoc, pi) insertedFlag = True currLoc += 1 break else: currLoc += 1 if insertedFlag == False: # PlanetaryInfo 'pi' has a timestamp that # is later than the last PlanetaryInfo in # 'planetData', so just append it. planetData.append(pi) # Increment currLoc so that the rest of # the PlanetaryInfos in # 'additionalPlanetaryInfos' can be # appended without doing anymore timestamp tests. currLoc += 1 # Do summations to determine the measurements. showGeocentricRetroAsNegativeTextFlag = True if showGeocentricRetroAsNegativeTextFlag == True: if tropicalZodiacFlag == True: totalDegrees = 0 zodiacType = "tropical" for i in range(len(planetData)): if i != 0: prevPi = planetData[i-1] currPi = planetData[i] if prevPi.geocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'] >= 0: # Direct motion. # Elapsed amount for this segment should be positive. # Find the amount of longitude elasped. longitudeElapsed = \ currPi.geocentric[zodiacType]['longitude'] - \ prevPi.geocentric[zodiacType]['longitude'] # See if there was a crossing of the # 0 degree point or the 360 degree point. # If so, make the necessary adjustments # so that the longitude elapsed is # correct. longitudeElapsed = \ Util.toNormalizedAngle(longitudeElapsed) totalDegrees += longitudeElapsed else: # Retrograde motion. # Elapsed amount for this segment should be negative. # Find the amount of longitude elasped. longitudeElapsed = \ currPi.geocentric[zodiacType]['longitude'] - \ prevPi.geocentric[zodiacType]['longitude'] # See if there was a crossing of the # 0 degree point or the 360 degree point. # If so, make the necessary adjustments # so that the longitude elapsed is # correct. if longitudeElapsed > 0: longitudeElapsed -= 360 totalDegrees += longitudeElapsed # Line of text. We append measurements to # this line of text depending on what # measurements are enabled. line = "G T {} moves ".format(planetName) numCircles = totalDegrees / circleSizeInDegrees numBiblicalCircles = \ totalDegrees / AstrologyUtils.degreesInBiblicalCircle line += "{:.2f} deg ".format(totalDegrees) # Append last part of the line. line += "(r as -)" text += line + os.linesep if centricityType == "heliocentric": if tropicalZodiacFlag == True: totalDegrees = 0 zodiacType = "tropical" for i in range(len(planetData)): if i != 0: prevPi = planetData[i-1] currPi = planetData[i] if prevPi.heliocentric[zodiacTypeForLongitudeSpeed]['longitude_speed'] >= 0: # Direct motion. # Elapsed amount for this segment should be positive. # Find the amount of longitude elasped. longitudeElapsed = \ currPi.heliocentric[zodiacType]['longitude'] - \ prevPi.heliocentric[zodiacType]['longitude'] # See if there was a crossing of the # 0 degree point or the 360 degree point. # If so, make the necessary adjustments # so that the longitude elapsed is # correct. longitudeElapsed = \ Util.toNormalizedAngle(longitudeElapsed) totalDegrees += longitudeElapsed else: # Retrograde motion. # Elapsed amount for this segment should be negative. # Find the amount of longitude elasped. longitudeElapsed = \ currPi.heliocentric[zodiacType]['longitude'] - \ prevPi.heliocentric[zodiacType]['longitude'] # See if there was a crossing of the # 0 degree point or the 360 degree point. # If so, make the necessary adjustments # so that the longitude elapsed is # correct. if longitudeElapsed > 0: longitudeElapsed -= 360 totalDegrees += longitudeElapsed # Line of text. We append measurements to # this line of text depending on what # measurements are enabled. line = "H T {} moves ".format(planetName) numCircles = totalDegrees / circleSizeInDegrees numBiblicalCircles = \ totalDegrees / AstrologyUtils.degreesInBiblicalCircle line += "{:.2f} deg ".format(totalDegrees) text += line + os.linesep text = text.rstrip() return totalDegrees
def getGeocentricPlanetDirectRetrogradeInfo(planetName): """ Returns a list of tuples, each tuple containing: (planetName, julianDay, datetime, "R" or "D", geoTropLongitudeOfPlanet, geoSidLongitudeOfPlanet) """ # Return value. rv = [] prevDt = None currDt = copy.deepcopy(startDt) prevTropLongitudeSpeed = None currTropLongitudeSpeed = None currTropLongitude = None currSidLongitude = None while currDt <= endDt: dt = currDt pi = Ephemeris.getPlanetaryInfo(planetName, dt) log.debug("Just obtained planetaryInfo for planet '{}', timestamp: {}".\ format(planetName, Ephemeris.datetimeToStr(dt))) # Get the geocentric longitude and geocentric longitude speed. tropLongitudeSpeed = pi.geocentric['tropical']['longitude_speed'] tropLongitude = pi.geocentric['tropical']['longitude'] sidLongitude = pi.geocentric['sidereal']['longitude'] # Store new current planet values. currTropLongitudeSpeed = tropLongitudeSpeed currTropLongitude = tropLongitude currSidLongitude = sidLongitude log.debug("prevTropLongitudeSpeed={}, currTropLongitudeSpeed={}".\ format(prevTropLongitudeSpeed, currTropLongitudeSpeed)) # We need two data points to proceed. if prevTropLongitudeSpeed != None and \ currTropLongitudeSpeed != None and \ prevDt != None: # Check to see if we passed over 0 degrees. if prevTropLongitudeSpeed < 0.0 and currTropLongitudeSpeed >= 0.0: # Crossed over from negative to positive! log.debug("Crossed over from negative to positive!") # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than the # desired threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. diffTd = t2 - t1 halfDiffTd = \ datetime.\ timedelta(days=(diffTd.days / 2.0), seconds=(diffTd.seconds / 2.0), microseconds=(diffTd.microseconds / 2.0)) testDt = t1 + halfDiffTd pi = Ephemeris.getPlanetaryInfo(planetName, testDt) testTropLongitudeSpeed = \ pi.geocentric['tropical']['longitude_speed'] testTropLongitude = pi.geocentric['tropical']['longitude'] testSidLongitude = pi.geocentric['sidereal']['longitude'] if testTropLongitudeSpeed >= 0.0: t2 = testDt # Update the curr values as the later boundary. currDt = t2 currTropLongitudeSpeed = testTropLongitudeSpeed currTropLongitude = testTropLongitude currSidLongitude = testSidLongitude else: t1 = testDt currErrorTd = t2 - t1 # Broke out of while loop, meaning we have a timestamp # within our threshold. # Create a tuple to add to our list. tup = (planetName, Ephemeris.datetimeToJulianDay(currDt), currDt, directStr, currTropLongitude, currSidLongitude) # Append to the list. rv.append(tup) elif prevTropLongitudeSpeed > 0.0 and currTropLongitudeSpeed <= 0.0: # Crossed over from positive to negative! log.debug("Crossed over from positive to negative!") # This is the upper-bound of the error timedelta. t1 = prevDt t2 = currDt currErrorTd = t2 - t1 # Refine the timestamp until it is less than the # desired threshold. while currErrorTd > maxErrorTd: log.debug("Refining between {} and {}".\ format(Ephemeris.datetimeToStr(t1), Ephemeris.datetimeToStr(t2))) # Check the timestamp between. diffTd = t2 - t1 halfDiffTd = \ datetime.\ timedelta(days=(diffTd.days / 2.0), seconds=(diffTd.seconds / 2.0), microseconds=(diffTd.microseconds / 2.0)) testDt = t1 + halfDiffTd pi = Ephemeris.getPlanetaryInfo(planetName, testDt) testTropLongitudeSpeed = \ pi.geocentric['tropical']['longitude_speed'] testTropLongitude = pi.geocentric['tropical']['longitude'] testSidLongitude = pi.geocentric['sidereal']['longitude'] if testTropLongitudeSpeed <= 0.0: t2 = testDt # Update the curr values as the later boundary. currDt = t2 currTropLongitudeSpeed = testTropLongitudeSpeed currTropLongitude = testTropLongitude currSidLongitude = testSidLongitude else: t1 = testDt currErrorTd = t2 - t1 # Broke out of while loop, meaning we have a timestamp # within our threshold. # Create a tuple to add to our list. tup = (planetName, Ephemeris.datetimeToJulianDay(currDt), currDt, retrogradeStr, currTropLongitude, currSidLongitude) # Append to the list. rv.append(tup) # Increment currDt timestamp. prevDt = currDt currDt = currDt + stepSizeTd # Move the previous currTropLongitudeSpeed to prevTropLongitudeSpeed. prevTropLongitudeSpeed = currTropLongitudeSpeed currTropLongitudeSpeed = None currTropLongitude = None currSidLongitude = None log.debug("prevTropLongitudeSpeed={}, currTropLongitudeSpeed={}".\ format(prevTropLongitudeSpeed, currTropLongitudeSpeed)) return rv
# node of this planet. helioSidLongitudeOfNorthNode = None if node == northStr: helioSidLongitudeOfNorthNode = helioSidLongitudeOfNode elif node == southStr: helioSidLongitudeOfNorthNode = \ Util.toNormalizedAngle(helioSidLongitudeOfNode + 180) else: log.error("Unknown node type.") shutdown(1) # Assemble the line that will go into the CSV file. line = "" line += "{}".format(planetName) + "," line += "{}".format(jd) + "," line += "{}".format(Ephemeris.datetimeToStr(dt)) + "," line += "{}".format(node) + "," line += "{}".format(helioTropLongitudeOfNode) + "," line += "{}".format(helioSidLongitudeOfNode) + "," line += "{}".format(helioTropLongitudeOfNorthNode) + "," line += "{}".format(helioSidLongitudeOfNorthNode) + "," # Remove last trailing comma. line = line[:-1] # Append to the output lines. outputLines.append(line) # Write outputLines to output file. with open(outputFilename, "w", encoding="utf-8") as f: log.info("Writing to output file '{}' ...".format(outputFilename))