More efficient solution to clocks offset - python

I am writing a program where the user inputs two values: the number of minutes fast per hour that two clocks are. The output should be the time displayed on the clocks when the two clocks are showing the same time. The clocks are only checked once an hour (according to an accurate clock).
At the moment, I have:
clock1 = 0
clock2 = 0
inp = input(">").split(" ")
offset1 = int(inp[0])+60
offset2 = int(inp[1])+60
def add():
global clock1
global clock2
clock1 += offset1
if clock1 > 1399:
clock1 -= 1440
clock2 += offset2
if clock2 > 1399:
clock2 -= 1440
add()
while clock1 != clock2:
add()
hours = 0
while clock1 > 59:
hours += 1
clock1 -= 60
while hours > 23:
hours -= 24
hours = str(hours)
minutes = str(clock1)
if len(hours) == 1:
hours = "0" + hours
if len(minutes) == 1:
minutes = "0" + minutes
print(hours + ":" + minutes)
This works, but when the input values get big, it takes too long. How can I make this solution more efficient?

Related

Check a condition every 3 minutes without functions and without interrupting the loop

I have this working code that checks a conditions every 3 minutes considering the local time, so every 0, 3, 6, 9.....It prints "checking condition".
import time
def get_next_time():
minute = time.localtime().tm_min
result = 3 - (minute % 3) + minute
if result == 60:
result = 0
return result
next_run = get_next_time()
while True:
now = time.localtime()
if next_run == now.tm_min:
print("checking condition")
#some condition
next_run = get_next_time()
time.sleep(1)
The problem is that I need the code without functions, so I need to find a way to write this code without using any funcion, and I cannot use break or interrput the loop
I tried:
while True:
minute = time.localtime().tm_min
result = 3 - (minute % 3) + minute
if result == 60:
result = 0
now = time.localtime()
if result == now.tm_min:
print("checking conditions")
time.sleep(1)
But it does not work: it does not do nothing.
Any ideas?
you can compact the function in one statement:
import time
next_run = (3 - (time.localtime().tm_min % 3) + time.localtime().tm_min)%60
while True:
now = time.localtime()
if next_run == now.tm_min:
print("checking condition")
#checking conditions...
next_run=(3 - (time.localtime().tm_min % 3) + time.localtime().tm_min)%60
time.sleep(1)
The first time, the get_next_time() will only be executed when next_run == now.tm_min. The second time, you execute it each loop
import time
minute = time.localtime().tm_min
result = 3 - (minute % 3) + minute
if result == 60:
result = 0
while True:
now = time.localtime()
if result == now.tm_min:
print("checking conditions")
minute = time.localtime().tm_min
result = 3 - (minute % 3) + minute
if result == 60:
result = 0
time.sleep(1)
Rounding to the next multiple of 3 minutes contradicts the specification "every 0...".
It is enough to do
import time
first= True
while True:
minute= time.localtime().tm_min
if first or minute == target:
print("checking condition")
first= False
target= (minute + 3) % 60
time.sleep(1)
Update:
I modified the code so that a single call to localtime is made on every iteration, to make fully sure that the minutes do not change between the calls.
More compact but less efficient:
import time
while True:
minute= time.localtime().tm_min
if 'target' not in locals() or minute == target:
print("checking condition")
target= (minute + 3) % 60
time.sleep(1)

Ultrasonic sensor as counter

I am trying to code ultrasonic sensors to count the number of cars in a parking lot. I am relatively new to Python, so I am asking here for help.
I have three parking slots, in which each of them has an ultrasonic sensor.
How do I make it so that the sensors and their counters work together? For example, when the parking slots are empty, the counter shows three parking slots available. When two parking slots are filled, the counter shows one availability, etc.
I have done the following code, and I am wondering how I could continue to achieve my objective?
# Sensor 1
def distance_1():
time.sleep(0.5)
GPIO.output(TRIG_1, True)
time.sleep(0.00001)
GPIO.output(TRIG_1, False)
print("Reading Sensor 1")
while GPIO.input(ECHO_1) == 0:
start = time.time()
while GPIO.input(ECHO_1) == 1:
end = time.time()
duration = end - start
sound = 34000 / 2
distance = duration * sound
round(distance, 0)
total = 3
count = total
if distance <= 10:
count -= 1
elif distance > 10:
count += 1
if count < 0:
count = 0
elif count > total:
count = total
print(count)
mylcd.lcd_display_string("{}".format(count), 2)
# Sensor 2
def distance_2():
time.sleep(0.5)
GPIO.output(TRIG_2, True)
time.sleep(0.00001)
GPIO.output(TRIG_2, False)
print("Reading Sensor 2")
while GPIO.input(ECHO_2) == 0:
start = time.time()
while GPIO.input(ECHO_2) == 1:
end = time.time()
duration = end - start
sound = 34000 / 2
distance = duration * sound
round(distance, 0)
total = 3
count = total
if distance <= 10:
count -= 1
elif distance > 10:
count += 1
if count < 0:
count = 0
elif count > total:
count = total
print(count)
mylcd.lcd_display_string("{}".format(count), 2)
# Sensor 3
def distance_3():
time.sleep(0.5)
GPIO.output(TRIG_3, True)
time.sleep(0.00001)
GPIO.output(TRIG_3, False)
print("Reading Sensor 3")
while GPIO.input(ECHO_3) == 0:
start = time.time()
while GPIO.input(ECHO_3) == 1:
end = time.time()
duration = end - start
sound = 34000 / 2
distance = duration * sound
round(distance, 0)
total = 3
count = total
if distance <= 10:
count -= 1
elif distance > 10:
count += 1
if count < 0:
count = 0
elif count > total:
count = total
print(count)
mylcd.lcd_display_string("{}".format(count), 2)
while True:
distance_1()
distance_2()
distance_3()
GPIO.cleanup()
The trouble with programming is there are so many ways to achieve the same result.
Looking at your code, I would suggest taking a step back and refactoring it to use Python classes instead. You have a lot of code repetition happening, and eventually, the code will break if you need to keep adding more sensors.
For example:
class Parking:
"This is a parking class"
def __init__(self, space):
self.space = space
def empty(self):
if self.space == 0:
print('Parking space is empty')
def full(self):
if self.space == 1:
print('Parking space is full')
def distance(self):
time.sleep(0.5)
GPIO.output(TRIG, True)
. . .
# Input:
sensor1 = Parking(1)
sensor2 = Parking(1)
sensor3 = Parking(0)
# Output:
sensor1.empty()
sensor2.empty()
sensor3.empty()
# Output:
sensor1.full()
sensor2.full()
sensor3.full()
You can then update a dictionary with the output to monitor the latest sensor information. Ideally, the dictionary would be written to a central file accessible by all the sensors or raspberry pis to read.
available_spaces = {"sensor1": 0, "sensor2": 1, "sensor3": 0}
I analyzed your code and I did some refactorings.
I suggest you to use constant values initialization (the config section you see in the code below). The values I used are random.
A function can be parametrized, so you can pass arguments to functions, and avoid writing the same piece of code changing only a few values in the same place.
You should set up your microcontroller when the script starts, to tell the board how you are using the pins (as input or output).
I didn't dig into on the snippet above the lcd_display_string and why are you doing those operations. I suppose that these are required to print on screen the distance.
## configuration
# trigger
TRIG_1 = 17
TRIG_2 = 27
TRIG_3 = 22
# echo
ECHO_1 = 10
ECHO_2 = 9
ECHO_3 = 11
# timings
INITIAL_DELAY = 0.5
TRIGGERING_DELAY = 0.00001
## support functions
# initializing GPIO
def set_up():
# set trigger GPIOs as output pins
GPIO.setup(TRIG_1, GPIO.OUT)
GPIO.setup(TRIG_2, GPIO.OUT)
GPIO.setup(TRIG_3, GPIO.OUT)
# set echo GPIOs as input pins
GPIO.setup(ECHO_1, GPIO.IN)
GPIO.setup(ECHO_2, GPIO.IN)
GPIO.setup(ECHO_3, GPIO.IN)
# I didn't dig into these values and why are you doing these operations. I suppose that these are required to print on screen the distance.
def print_distance_on_lcd(distance):
total = 3
count = total
if distance <= 10:
count -= 1
elif distance > 10:
count += 1
if count < 0:
count = 0
elif count > total:
count = total
print(count)
mylcd.lcd_display_string("{}".format(count), 2)
def trigger(trigger):
time.sleep(INITIAL_DELAY)
GPIO.output(trigger, True) # set output pin on HIGH state
time.sleep(TRIGGERING_DELAY)
GPIO.output(trigger, False) # set output pin on LOW state
def distance(t, echo):
trigger(t)
# initializing the variables here, allows you to use it outside the while block below
# using variable names that explains their content
start_time = time.time()
end_time = time.time()
# this block is not wrong, but unnecessary: initializing the variable like above is enough
'''
while GPIO.input(echo) == 0:
start_time = time.time()
'''
while GPIO.input(echo) == 1:
end_time = time.time()
duration = end_time - start_time
sound = 34000 / 2
distance = duration * sound
return distance
# call initialization function (this will be executed only one time)
set_up()
# loop forever
while True:
set_up()
print("Reading Sensor 1")
distance_sensor_1 = distance(TRIG_1, ECHO_1)
print_distance_on_lcd(distance_sensor_1)
print("Reading Sensor 2")
distance_sensor_2 = distance(TRIG_2, ECHO_2)
print_distance_on_lcd(distance_sensor_2)
print("Reading Sensor 3")
distance_sensor_3 = distance(TRIG_3, ECHO_3)
print_distance_on_lcd(distance_sensor_3)
GPIO.cleanup()

Multiprocessing with alarm

I've been working on a personal assistant in python for a long time now and I've come across a problem that I can't solve.
I want to add a reminder.
I want to combine the alarm code and assistant code in two different loops.
When I set the alarm timer I want to do the other input tasks until the timer runs out.
How can I do that? Thanks.
I've tried this code, but it doesn't work because I only have one loop:
import datetime
import os
import time
import random
def assistant():
command = input('command: ')
if command == '1':
print('is it one')
elif command == '2':
print('is it two')
elif command == 'alarm':
import datetime
def check_alarm_input(alarm_time):
if len(alarm_time) == 1: # [Hour] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0:
return True
if len(alarm_time) == 2: # [Hour:Minute] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0 and \
alarm_time[1] < 60 and alarm_time[1] >= 0:
return True
elif len(alarm_time) == 3: # [Hour:Minute:Second] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0 and \
alarm_time[1] < 60 and alarm_time[1] >= 0 and \
alarm_time[2] < 60 and alarm_time[2] >= 0:
return True
return False
# Get user input for the alarm time
print("Set a time for the alarm (Ex. 06:30 or 18:30:00)")
while True:
alarm_input = input(">> ")
try:
alarm_time = [int(n) for n in alarm_input.split(":")]
if check_alarm_input(alarm_time):
break
else:
raise ValueError
except ValueError:
print("ERROR: Enter time in HH:MM or HH:MM:SS format")
# Convert the alarm time from [H:M] or [H:M:S] to seconds
seconds_hms = [3600, 60, 1] # Number of seconds in an Hour, Minute, and Second
alarm_seconds = sum([a*b for a,b in zip(seconds_hms[:len(alarm_time)], alarm_time)])
# Get the current time of day in seconds
now = datetime.datetime.now()
current_time_seconds = sum([a*b for a,b in zip(seconds_hms, [now.hour, now.minute, now.second])])
# Calculate the number of seconds until alarm goes off
time_diff_seconds = alarm_seconds - current_time_seconds
# If time difference is negative, set alarm for next day
if time_diff_seconds < 0:
time_diff_seconds += 86400 # number of seconds in a day
# Display the amount of time until the alarm goes off
print("Alarm set to go off in %s" % datetime.timedelta(seconds=time_diff_seconds))
# Sleep until the alarm goes off
time.sleep(time_diff_seconds)
# Time for the alarm to go off
print("Wake Up!")
while True:
assistant()
1.Reminder code:
import datetime
import os
import time
import random
def check_alarm_input(alarm_time):
"""Checks to see if the user has entered in a valid alarm time"""
if len(alarm_time) == 1: # [Hour] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0:
return True
if len(alarm_time) == 2: # [Hour:Minute] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0 and \
alarm_time[1] < 60 and alarm_time[1] >= 0:
return True
elif len(alarm_time) == 3: # [Hour:Minute:Second] Format
if alarm_time[0] < 24 and alarm_time[0] >= 0 and \
alarm_time[1] < 60 and alarm_time[1] >= 0 and \
alarm_time[2] < 60 and alarm_time[2] >= 0:
return True
return False
# Get user input for the alarm time
print("Set a time for the alarm (Ex. 06:30 or 18:30:00)")
while True:
alarm_input = input(">> ")
try:
alarm_time = [int(n) for n in alarm_input.split(":")]
if check_alarm_input(alarm_time):
break
else:
raise ValueError
except ValueError:
print("ERROR: Enter time in HH:MM or HH:MM:SS format")
# Convert the alarm time from [H:M] or [H:M:S] to seconds
seconds_hms = [3600, 60, 1] # Number of seconds in an Hour, Minute, and Second
alarm_seconds = sum([a*b for a,b in zip(seconds_hms[:len(alarm_time)], alarm_time)])
# Get the current time of day in seconds
now = datetime.datetime.now()
current_time_seconds = sum([a*b for a,b in zip(seconds_hms, [now.hour, now.minute, now.second])])
# Calculate the number of seconds until alarm goes off
time_diff_seconds = alarm_seconds - current_time_seconds
# If time difference is negative, set alarm for next day
if time_diff_seconds < 0:
time_diff_seconds += 86400 # number of seconds in a day
# Display the amount of time until the alarm goes off
print("Alarm set to go off in %s" % datetime.timedelta(seconds=time_diff_seconds))
# Sleep until the alarm goes off
time.sleep(time_diff_seconds)
# Time for the alarm to go off
print("Wake Up!")
2.Representative assistant code:
def assistant():
command = input('command: ')
if command == '1':
print('is it one')
elif command == '2':
print('is it two')
while True:
assistant()
I am planning such an output
command: 1
is it one
command: 2
is it two
command: alarm
Set a time for the alarm (Ex. 06:30 or 18:30:00)
>> 13:00
Alarm set to go off in 1:30:00
command: 1
is it one
command: 2
is it two
Wake Up!
command: 1
is it one
command: 2
is it two

How do I transfer over the seconds to minutes, and minutes to hrs

class time:
def __init__(self,hrs,mins,secs):
self.hrs = hrs
self.mins= mins
self.secs = secs
def __str__(self):
return (str(self.hrs%24)+":"+str(self.mins)+":"+str(self.secs))
def __repr__(self):
return (str(self.hrs%24)+":"+str(self.mins)+":"+str(self.secs))
When I do input the time, like when I put t = time(0,0,90).
How do I get the code to return 0:1:30?
How would I do the below also too?
Add a method called increment. This should add one second to the current time. Make sure that you check all the boundary conditions. When the time is 23:59:59 is incremented, it should roll over to 00:00:00.
I'd go a totally different route. Presenting it here in case it helps you.
class Time:
def __init__(self, hh, mm, ss):
self.time = hh*3600 + mm*60 + ss # Total seconds
#property
def hours(self):
return (self.time % 86400) // 3600
#property
def mins(self):
return (self.time % 3600) // 60
#property
def secs(self):
return self.time % 60
def increment(self):
self.time += 1
This is, imo, the simplest and most straightforward way to do it. If you've got multiple values that have to stay in sync, my advice is not to store them separately: just store them together in the underlying system, and separate them out when the user wants them.
When I do input the time, like when I put t = time(0,0,90). How do I get the code to return 0:1:30?
This can be done by simply moving the seconds up to minutes and minutes up to hours, scaling as necessary. Start with:
hh = 0
mm = 0
ss = 90
and the code to do it is a relatively simple:
while ss >= 60:
ss -= 60
mm += 1
while mm >= 60:
mm -= 60
hh = (hh + 1) % 24
Note that's not the most efficient way to do it, especially if the value can be high. If that's a possibility, you're better off with something like:
mm += (ss // 60) ; ss = ss % 60
hh += (mm // 60) ; mm = mm % 60
hh = hh % 24
Add a method called increment. This should add one second to the current time. Make sure that you check all the boundary conditions. When the time is 23:59:59 is incremented, it should roll over to 00:00:00.
Following a similar pattern (and assuming you've already fixed it to ensure values are properly clamped), this is also relatively easy.
ss = (ss + 1) % 60 # add second, wrapping if needed.
if ss == 0: # if second wraps, do same to minute.
mm = (mm + 1) % 60
if mm == 0: # if minute wraps, increment hour.
hh = (hh + 1) % 24
Note that I haven't integrated that into your specific code, I'm more treating Python as a pseudo-code language (at which it excels). That's because it's classwork and you'll learn a lot more by doing it yourself (once you know how, of course).
You should take the algorithms I've provided and adapt them for your own code.
I recommend you make a method to add an hour min and sec and the increment method just calls the add sec method. In the methods they handle when they reach their respective limits.
class time:
def __init__(self,hrs,mins,secs):
self.secs = secs % 60
self.mins = (mins + (secs // 60)) % 60
self.hrs = (hrs + (mins // 60)) % 24
def add_hour(self):
if self.hrs == 23:
self.hrs = 0
else:
self.hrs += 1
def add_min(self):
if self.mins == 59:
self.add_hour()
self.mins = 0
else:
self.mins += 1
def add_sec(self):
if self.secs == 59:
self.add_min()
self.secs = 0
else:
self.secs += 1
def increment(self):
self.add_sec()
def __str__(self):
return (f"{self.hrs}:{self.mins}:{self.secs}")
def __repr__(self):
return (f"{self.hrs}:{self.mins}:{self.secs}")
t = time(23,59,59)
print(t)
#23:59:59
t.increment()
print(t)
#0:0:0
t = time(0,0,90)
print(t)
#0:1:30
You can run this code yourself here

Timer in Pygame

I'm completely new to Python and I'm trying to set a timer in this game I have... Everything else has been fine but this timer is a headache.
I'll only post the parts associated with the timer to make it easier.
frame_count = 0
second = 0
minute = 5
hour = 1
time = "1 5. 0"
And then in my main loop I have.
font = pygame.font.SysFont('DS-Digital', 50, False, False)
text = font.render(time,True,red)
display.blit(text, [302, 50])
frame_count += 1
if frame_count == 60:
frame_count = 0
second -= 1
elif second == 0:
second = 9
minute -= 1
elif minute == 0:
minute = 9
hour -= 1
elif second == 0 and minute == 0 and hour == 0:
second = 0
minute = 0
hour = 0
hour = time[0]
minute = time[2]
second = time[5]
clock.tick(60)
This gives me back an error for being wrong type but I've tried converting to int and vice versa... So frustrating...
I've looked at so many examples but most examples are actual minutes and seconds.
I need my right number to just countdown from 9 to 0 then minus off middle number and so forth.
If this your whole code then you didn't imported pygame. (import pygame) at the begining and you should loop it all so for example:
import pygame
while True:
...
Your code
...
Specify more your question please, I see that you should use if insted of elif because once it got to 1 1 0 it will turn to 1 0 9 and then into 0 9 9 in two frames.
elif second == 0 and minute == 0 and hour == 0:
second = 0
minute = 0
hour = 0
This doesn't make sence really it's like if you calculate
if I have a = 0 then do a = 0 if you know what I mean (it does nothing).
Edit:
There is working code, you can edit it and replace your old code with it.
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 500))
red = (255, 0, 0)
bg_color = (0, 0, 0)
frame_count = 0
time = "1 5. 0"
while True:
pygame.time.Clock().tick(60)
frame_count += 1
hour = int(time[0])
minute = int(time[2])
second = int(time[5])
if second > 0 and frame_count == 20:
frame_count = 0
second -= 1
if second == 0 and minute > 0 and frame_count == 20:
frame_count = 0
second = 9
minute -= 1
if minute == 0 and hour > 0 and frame_count == 20:
frame_count = 0
minute = 9
second = 9
hour -= 1
time = str(hour) + " " + str(minute) + ". " + str(second)
font = pygame.font.SysFont('DS-Digital', 50, False, False)
text = font.render(time, True, red)
screen.fill(bg_color)
screen.blit(text, (302, 50))
pygame.display.update()
I'm quite sure that there is easier solution or more pythonic one, but it works and that is most important.
Remove these lines:
hour = time[0]
minute = time[2]
second = time[5]
it should work without them. Then ask what you are attempting to do with them
When you assign elements from time to you hour, minute, second variables convert them to an int like this:
hour = int(time[0])
minute = int(time[2]
second = int(time[5])
Here is my solution for simple way to sleep at the end of each iteration for the leftover time for a desired update rate
from timeit import default_timer as timer
from time import sleep as sleep
class loop_timer():
""" simple game loop timer that sleeps for leftover time (if any) at end of each iteration"""
LOG_INTERVAL_SEC=10
def __init__(self, rate_hz:float):
''' :param rate_hz: the target loop rate'''
self.rate_hz=rate_hz
self.start_loop()
self.loop_counter=0
self.last_log_time=0
def start_loop(self):
""" can be called to initialize the timer"""
self.last_iteration_start_time=timer()
def sleep_leftover_time(self):
""" call at start or end of each iteration """
now=timer()
max_sleep=1./self.rate_hz
leftover_time=max_sleep-(now-self.last_iteration_start_time)
if leftover_time>0:
sleep(leftover_time)
self.start_loop()
self.loop_counter+=1
if now-self.last_log_time>self.LOG_INTERVAL_SEC:
self.last_log_time=now
if leftover_time>0:
print('loop_timer slept for {:.1f}ms leftover time for desired loop interval {:.1f}ms'.format(leftover_time*1000,max_sleep*1000))
else:
print('loop_timer cannot achieve desired rate {}Hz, time ran over by {}ms compared with allowed time {}ms'.format(self.rate_hz, -leftover_time*1000, max_sleep*1000))
Use it like this
looper=loop_timer(MODEL_UPDATE_RATE_HZ)
while not self.exit:
# do your stuff here
try:
looper.sleep_leftover_time()
except KeyboardInterrupt:
logger.info('KeyboardInterrupt, stopping')
self.exit=True
continue

Categories

Resources