Plot time vs angle; how to convert time into a number - excel

I am trying create a plot(angle of incidence vs. time). Time is set between hour of sunrise to hour of sunrise (6:37:00 AM - 6:39:00 PM). I have to find angle of incidence for each minute interval starting from sunrise to sunset. The only issue is I don't have the faintest clue how to convert time into a number.
Angle of incidence depends on hour angle (Angle_hour). This is dependent on the time. Time before noon is given a negative value, and time after noon is positive. For example, at 6:37 am, the Hours would equal -6.62. On the other hand, 6:39 PM would equal 6.65. I am trying to have a for loop calculate the different values within the time frame.
for k = 1:6
Hours = k;
Angle_Hour(k) = 15 * Hours;
Angle_Incidence(k) = acos((sin(Angle_Declination) * sin (Angle_Latitude) * cos(Angle_Slope)) - (sin(Angle_Declination) * cos(Angle_Latitude) * sin(Angle_Slope) * cos(Angle_SurfaceAzimuth)) + (cos(Angle_Declination) * cos(Angle_Latitude) * cos(Angle_Slope) * cos(Angle_Hour(k))) + (cos(Angle_Declination) * sin(Angle_Latitude) * sin(Angle_Slope) * cos(Angle_SurfaceAzimuth) * cos(Angle_Hour(k))) + (cos(Angle_Declination) * sin(Angle_Slope) * sin(Angle_SurfaceAzimuth) * sin(Angle_Hour(k)))) ;
end

If in your program the time in a day is a variable of type datetime, then you can either use datenum to turn the date to a number, or you can use the functions: hour, minute, second to extract the hours, minutes and seconds, and then calculate the angle using them.
So for example, you can have something like this:
function angle = Angle_Hour(k)
hours = hour(k) + minute(k)/60 + second(k)/3600
angle = % some expression/function of time in hours
end

Related

What is the most efficient way print first K digits of a factorial without calculating the whole factorial?

My K has to be around 15,
and the number of which i am tring to get first k of its factorial is ~10^9.
Essentially I need to compute first 15 digits of n! where <= 1000000000 in under a second or so.
Everyone says its easy in python but I am still not able to .
My attempt using Stirlings approximation is only printing first 4 digits correctly. Let alone efficiency. :'(
import * from decimal
import *from math
getcontext().prec = 100
n = int(input())
x = Decimal("10") ** Decimal(str(Decimal(str(n)) * Decimal(str(n)).log10() - Decimal(str(n)) * Decimal(e).log10()))
print(x * Decimal(Decimal("2") * Decimal(str(n)) * Decimal(pi)).sqrt())
Tried increasing precision of Decimal more but doesnt make any difference.

How to convert a continuous numbered array into a timestamp

I am gathering data on a device, and after every second, I update a count and log it. I am now processing it, and am new to python, so I had a question as to whether it was possible to convert a numbered array [0,1,2,3,4,...1091,1092,1093,...] into a timestamp [00:00:01, 00:00:02, 00:00:03, 00:00:04, ... 00:18:11, 00:18:12, 00:18:13,...] for example.
If you could please lead me in the right direction, that would be very much appreciated!
p.s. In the future, I will be logging the data as a timestamp, but for now, I have 5 hours' worth of data that needs to be processed!
import datetime as dt
timestamp=[0,1,2,3,4,5,1092,1093]
print([dt.timedelta(seconds=ts) for ts in timestamp])
Happy Coding
If all you have is seconds, then you can just do simple arithmetic to convert them to minutes and hours:
inp = [0, 1, 2, 3, 4, 1091, 1092, 1093]
outp = [f'{secs // 3600:02}:{(secs // 60) % 60:02}:{secs % 60:02}' for secs in inp]
print(outp)
# ['00:00:00', '00:00:01', '00:00:02', '00:00:03', '00:00:04', '00:18:11', '00:18:12', '00:18:13']
Here, I use a list comprehension and, for each secs in the input, create a format string:
hours is secs // 3600 (that's integer floor division), because one hour is 3600 seconds
Minutes is (secs // 60) % 60 (this incorporates the modulo operator, which displays the remainder of secs // 60 after dividing it by 60 again). One minute is 60 seconds, but more than 60 minutes would be an hour, so we need to make sure to 'roll over' the counter every 60 minutes (which is what the mod is for).
Seconds is, of course, secs % 60, because a minute has 60 seconds and we want the counter to roll over.
The format string starts with f', and anything inside {} is an instruction to evaluate whatever's inside it, and insert that into the string. The syntax is {expression:format}, where display is an optional instruction for how to format the data (i.e. not just printing it out). And format can get complicated (look up a python f-string tutorial if you're curious about the specifics), but suffice it to say that in this case we use 02, which means that we want the output to be two characters in length, and padded with zeroes in case it's less than that.

Is there a way i can use datetime to convert a string to milliseconds without using timedelta?

I'm trying to convert a string containing a time ("%H:%M:%S.%f") to an int of the equivalent milliseconds. The complication is, the time is the output from FFmpeg, it's a point in the audio file. I need to get the number of milliseconds the time in the string represents. The timestamp method in DateTime is milliseconds from epoche, without another time stamp from when I began, this is no good.
For example:
t = "00:05:52.654321"
should be converted to:
i = 352654321
What is the best way to accomplish this?
This is how I figured out to do it.
def _convert_string_to_int(self, s) -> int:
begin = datetime.datetime(1900,1,1)
end = datetime.datetime.strptime(s, self._ffmpeg_format_string)
return int((end - begin).total_seconds() * 1000000)
It just feels really unnecessary to use timedelta like that.
Since timestamps are relative to the Unix Epoch (1970-01-01) you can make a datetime object from your time by prepending that date to it and then getting the timestamp of the resultant object to get the time string converted to seconds . Since python timestamps are floating point representations of seconds since the epoch, you will need to multiply by 1000 and convert to integer to get the number of milliseconds:
from datetime import datetime
t = "00:05:52.654321"
d = datetime.strptime('1970-01-01 ' + t, '%Y-%m-%d %H:%M:%S.%f')
print(int(d.timestamp()*1000))
Output:
352654
If you actually want microseconds, multiply by 1000000 instead.
As an alternative, you can split the time string on : and sum the parts, multiplying by 60 or 3600 to convert the hour and minute parts to seconds:
t = "00:05:52.654321"
millisecs = int(sum([float(v) * 1000 * 60 ** (2 - i) for i, v in enumerate(t.split(':'))]))
print(millisecs)
Output:
352654
Again, if you want microseconds, just multiply by 1000000 instead of 1000.
A number of milliseconds is inherently a time interval, so there is good reason why datetime.timedelta instances have a total_seconds method while datetime.datetime, datetime.date and datetime.time do not have one.
In principle you could use datetime.datetime.time(end) to get an object with properties including hour, minute, second and microsecond, and then use these to construct an arithmetic expression for the elapsed time since midnight on the same day. However, the supported way to handle time intervals like this is precisely the timedelta approach that you are already using.

How to improve readability of code that transforms seconds to hours, minutes and seconds format

I got the code to do what I want, yet I feel its hardcoded than an actual solution. Any suggestions on how I could adjust the Hours, Minutes, Seconds variables to be more clear to the reader?
Input = int(input("Seconds: "))
Hours = Input // (60*60)
Minutes = Input//(60) - (Hours*60)
Seconds = Input - (Minutes*60) - (Hours*60*60)
print(Hours,"Hours",Minutes,"Minutes",Seconds,"Seconds")
Use modulos instead of division. They're a little confusing at first, but they're really awesome.
def convert(seconds):
secs = seconds%60
mins = (seconds//60)%60
hrs = (seconds//3600)
return (secs,mins,hrs)
From a code optimization standpoint, my code does a total of four arithmetic operations, whereas yours runs through 10. Additionally, the whole (Hours * 60) thing is a little difficult to understand.
That's not to say your code is bad, just a little unclear. Though readability counts, your code is not so illegible as to be impossible to understand.
Constants help readability. Also using modulo helps:
SEC_PER_MIN = 60
SEC_PER_HOUR = SEC_PER_MIN * 60
secs = int(input("Seconds: "))
hours = secs // SEC_PER_HOUR
remaining_seconds = secs % SEC_PER_HOUR
mins = remaining_seconds // SEC_PER_MIN
remaining_seconds %= SEC_PER_MIN
print(f"{hours} Hours, {mins} Minutes, and {remaining_seconds} Seconds")
or you can abuse the time module and have it handle all the logic:
import time
secs = int(input("Seconds: "))
result = time.strftime('%H Hours, %M Minutes, %S Seconds', time.gmtime(secs))
print(result)

Resolving Excel VBA time millisecond inaccuracy

I'm using excel time format "hh:mm:ss.000" and adding 50ms at a time to cells in consecutive rows via VBA:
Dim dblTimestamp As Double
dblTimestamp = Selection.Value ' origin timestamp
' Setup base time increment of 50ms over 20 minutes
For i = 1 To Selection.Rows.Count
Selection.Rows(i).Value2 = dblTimestamp + (2# / (864000# * 4#))
dblTimestamp = dblTimestamp + (2# / (864000# * 4#))
Next i
So you see the origin time below on 5/23/2015 and things start out fine:
5/23/2015 05:30:00.000
05:30:00.050
05:30:00.100
05:30:00.150
05:30:00.200
05:30:00.250
The problem is that the precision/rounding errors start to show up after a couple minutes (~1840 rows):
05:31:32.100
05:31:32.149
05:31:32.199
05:31:32.249
And then after 20 minutes it's more pronounced:
05:49:59.793
05:49:59.843
05:49:59.893
05:49:59.943
05:49:59.993
Can I use some other datatype for my calculations or do I have to brute force and add an extra millisecond every ~1840 rows?
I'd prefer a solution that would also apply when I change the time step to 200ms
This should do the trick. Note that I removed your "selection" reference and am instead using "Now()" as the time stamp and placing values in cells A2 through A20000. Functionally, you could combine all the time helper stuff into a single rounding function, but I designed it the way it is to feel more object oriented and to demonstrate a paradigm that's more adaptable. Hope this helps.
'A type used to store time data
Type TimeHelper
MS As Double
BaseTime As Double
End Type
'Value to use as millisecond
Const MilSec = 1 / 86400000
Sub Test()
Dim t As Double
t = Now()
Dim step As Double
step = 75
Dim TH As TimeHelper
For i = 2 To 200000
t = t + step * MilSec
TH = GetTimeHelper(t)
t = RoundMS(TH, step)
Cells(i, 1).Value2 = t
Next i
End Sub
Function GetTimeHelper(t As Double) As TimeHelper
x = t
'Unrounded Hours
x = (x - Round(x, 0)) * 24
'Unrounded Minutes
x = (x - Round(x, 0)) * 60
'Seconds as Milliseconds
GetTimeHelper.MS = (x - Round(x, 0)) * 60000
'Time rounded down to nearest minute by removing millisecond value
GetTimeHelper.BaseTime = t - (GetTimeHelper.MS * MilSec)
End Function
Function RoundMS(TH As TimeHelper, m As Double)
'Construct a time from basetime and milliseconds
'with milliseconds rounded to nearest multiple of m
RoundMS = TH.BaseTime + (Round(TH.MS / m, 0) * m) * MilSec
End Function
You need to round your date value after you have done the addition. Excel dates are stored as numbers under the hood and time is represented by a decimal. For example, 42249.6282730324 is 02/09/2015 (< to the left of the decimal) 15:04:43.550 (< to the right of the decimal) So you need to round this number. Here is a good post showing how you can do this using the INT, CEILING and MOD functions. http://exceluser.com/formulas/roundtime.htm.
I actually just decided to check the text value after every row to see if it ended in a 9 and then add a millisecond if necessary:
Dim dblTimestamp As Double
dblTimestamp = Selection.Value ' origin timestamp
' Setup base time increment of 50ms over 20 minutes
For i = 1 To Selection.Rows.Count
Selection.Rows(i).Value2 = dblTimestamp + (2# / (864000# * 4#))
dblTimestamp = dblTimestamp + (2# / (864000# * 4#))
' check to see if previous value ended in 9 indicating loss of precision
' e.g. 05:30:00.999 instead of 05:30:01.000
If Right(Selection.Rows(i).Cells(1).Text,1)="9") Then
dblTimestamp = dblTimestamp + (1#/86400000#) ' add 1 ms
Selection.Rows(i).Value2 = dblTimestamp
End If
Next i
This was good enough for my situation but P57's answer should still be good enough for other situations.

Resources