Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telemetry for capture module #73

Draft
wants to merge 11 commits into
base: dev
Choose a base branch
from
6 changes: 3 additions & 3 deletions RMS/Astrometry/SkyFit.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@

from RMS.Astrometry.ApplyAstrometry import xyToRaDecPP, raDecToXYPP, \
rotationWrtHorizon, rotationWrtHorizonToPosAngle, computeFOVSize, photomLine, photometryFit, \
rotationWrtStandard, rotationWrtStandardToPosAngle, correctVignetting, extinctionCorrectionTrueToApparent,
getFOVSelectionRadius
rotationWrtStandard, rotationWrtStandardToPosAngle, correctVignetting, \
extinctionCorrectionTrueToApparent, getFOVSelectionRadius
from RMS.Astrometry.AstrometryNetNova import novaAstrometryNetSolve
from RMS.Astrometry.Conversions import date2JD, JD2HourAngle
import RMS.ConfigReader as cr
Expand Down Expand Up @@ -3264,4 +3264,4 @@ def showAstrometryFitPlots(self):


plt.tight_layout()
plt.show()
plt.show()
76 changes: 69 additions & 7 deletions RMS/BufferedCapture.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import re
import time
import datetime
import logging
from multiprocessing import Process, Event

import cv2

from RMS.Misc import ping
from RMS.TelemetryServer import TelemetryServer

# Get the logger from the main module
log = logging.getLogger("logger")
Expand All @@ -34,6 +36,18 @@ class BufferedCapture(Process):
"""

running = False
telemetry = None

telemetry_data = {
'status': 'init',
'fps_expected' : 0,
'fps_estimated' : 0,
'block_frames' : 0,
'dropped_frames' : 0,
'frame_time_average' : 0,
'capture_start_time' : '1900-01-01T00:00:00.000000',
'last_frame_block' : '1900-01-01T00:00:00.000000',
}

def __init__(self, array1, startTime1, array2, startTime2, config, video_file=None):
""" Populate arrays with (startTime, frames) after startCapture is called.
Expand Down Expand Up @@ -66,7 +80,29 @@ def __init__(self, array1, startTime1, array2, startTime2, config, video_file=No
self.time_for_drop = 1.5*(1.0/config.fps)

self.dropped_frames = 0


def startTelemetry(self):
# Start telemetry web service
# TODO: put host and port on config file. Enable/disable on config file
try:
self.telemetry = TelemetryServer('localhost', 2100)
self.telemetry.set_data(self.telemetry_data)
self.telemetry.run()
except:
log.error('Error starting telemetry web service!')
self.telemetry = None

def stopTelemetry(self):
if self.telemetry is not None:
self.telemetry.shutdown()

def updateTelemetry(self):
if self.telemetry is not None:
self.telemetry.set_data(self.telemetry_data)

def updateTelemetryFrame(self, frame):
if self.telemetry is not None:
self.telemetry.set_data_frame(frame)


def startCapture(self, cameraID=0):
Expand Down Expand Up @@ -185,6 +221,12 @@ def initVideoDevice(self):
def run(self):
""" Capture frames.
"""

# number of frames in each capture block
block_frames = 256

# Start telemetry web service
self.startTelemetry()

# Init the video device
device = self.initVideoDevice()
Expand Down Expand Up @@ -216,6 +258,11 @@ def run(self):
log.info('Video device opened!')


self.telemetry_data['capture_start_time'] = datetime.datetime.utcnow().isoformat()
self.telemetry_data['block_frames'] = block_frames
self.telemetry_data['fps_expected'] = self.config.fps
self.telemetry_data['status'] = 'capturing'
self.updateTelemetry()

# Throw away first 10 frame
for i in range(10):
Expand Down Expand Up @@ -277,10 +324,10 @@ def run(self):


t_frame = 0
t_frame_total = 0
t_assignment = 0
t_convert = 0
t_block = time.time()
block_frames = 256

log.info('Grabbing a new block of {} frames...'.format(block_frames))
for i in range(block_frames):
Expand All @@ -289,6 +336,7 @@ def run(self):
t1_frame = time.time()
ret, frame = device.read()
t_frame = time.time() - t1_frame
t_frame_total += t_frame


# If the video device was disconnected, wait for reconnection
Expand Down Expand Up @@ -381,6 +429,21 @@ def run(self):



estimated_fps = round(block_frames/(time.time() - t_block),1)
if self.config.report_dropped_frames:
log.info('Estimated FPS: {:.2f}'.format(estimated_fps))

# update telemetry web service data
self.telemetry_data['fps_estimated'] = estimated_fps
self.telemetry_data['dropped_frames'] = self.dropped_frames
self.telemetry_data['frame_time_average'] = round(t_frame_total/block_frames, 3)
self.telemetry_data['last_frame_block'] = datetime.datetime.utcnow().isoformat()
self.updateTelemetry()
self.updateTelemetryFrame(frame) # copy the latest frame read

# Switch the frame block buffer flags
first = not first

if self.exit.is_set():
wait_for_reconnect = False
log.info('Capture exited!')
Expand All @@ -400,13 +463,12 @@ def run(self):
log.info('New block of raw frames available for compression with starting time: {:s}'.format(str(startTime)))


# Switch the frame block buffer flags
first = not first
if self.config.report_dropped_frames:
log.info('Estimated FPS: {:.3f}'.format(block_frames/(time.time() - t_block)))




log.info('Releasing video device...')
device.release()
log.info('Video device released!')


self.stopTelemetry()
92 changes: 92 additions & 0 deletions RMS/TelemetryServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
""" Generic Telemetry web service """

from http.server import BaseHTTPRequestHandler, HTTPServer, ThreadingHTTPServer
import json
from socketserver import BaseRequestHandler
import time
import threading
import re
import cv2
from typing import Callable, Tuple

class TelemetryServer(ThreadingHTTPServer):
data = {}
data_frame = None

def __init__(self, ip, port):
super().__init__((ip, port), TelemetryHandler)


def set_data(self, data_obj):
self.data = data_obj

def set_data_frame(self, data_frame_obj):
self.data_frame = data_frame_obj

def run(self):
server_thread = threading.Thread(target=self.serve_forever)
server_thread.daemon_threads = True
server_thread.start()

class TelemetryHandler(BaseHTTPRequestHandler):

def do_GET(self):
m = re.search('GET (/.*) HTTP.+', self.requestline)
if m:
self.handle_request(m.group(1))
else:
self.send_response(500)
self.wfile.write('Internal Error - parsing requestline')

def handle_request(self, req):

if req == '/':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()

self.wfile.write(bytes(json.dumps(self.server.data), "utf-8"))
self.wfile.flush()
elif req == '/last_frame':
if self.server.data_frame is None:
self.send_response(440)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(bytes('No data', 'utf-8'))
self.wfile.flush()
else:
self.send_response(200)
self.send_header('Content-type', 'image/jpeg')
self.end_headers()

_, frame = cv2.imencode('.JPEG', self.server.data_frame)

self.wfile.write(frame)
self.wfile.flush()

else:
self.send_response(440)
self.send_header('Content-type', 'text/plain')
self.end_headers()

self.wfile.write(bytes('Not found', 'utf-8'))
self.wfile.flush()




if __name__ == "__main__":
server = TelemetryServer('127.0.0.1', 5000)
server.run()

print("server started")
server.set_data({'hello': 'world', 'received': 'ok'})


input("Press any key to terminate the program")
server.set_data({'hello': 'world', 'received': 'err'})
input("Press any key to terminate the program")

server.shutdown()
print("Server stopped.")

2 changes: 1 addition & 1 deletion Utils/FFtoFrames.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def FFtoFrames(file_path, out_dir, file_format, deinterlace_mode, first_frame=0,

# No deinterlace
else:
frame_name, frame_dt = saveFrame(frame, i-first_frame, out_dir, file_name_saving, file_format, ff_dt, fps)
frame_name, frame_dt = saveFrame(frame, i, out_dir, file_name_saving, file_format, ff_dt, fps)
frame_name_time_list.append([frame_name, frame_dt])

# If the frames are saved for METAL, the times have to be given in a separate file
Expand Down
6 changes: 3 additions & 3 deletions Utils/GenerateMP4s.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def generateMP4s(dir_path, ftpfile_name):
root = os.path.dirname(__file__)
ffmpeg_path = os.path.join(root, "ffmpeg.exe")
# Construct the ecommand for ffmpeg
com = ffmpeg_path + " -y -f image2 -pattern_type sequence -i " + temp_img_path +" " + mp4_path
com = ffmpeg_path + " -y -f image2 -pattern_type sequence -start_number " + str(first_frame) + " -i " + temp_img_path +" " + mp4_path
print("Creating timelapse using ffmpeg...")
else:
# If avconv is not found, try using ffmpeg
Expand All @@ -122,12 +122,12 @@ def generateMP4s(dir_path, ftpfile_name):
if os.system(software_name + " --help > /dev/null"):
software_name = "ffmpeg"
# Construct the ecommand for ffmpeg
com = software_name + " -y -f image2 -pattern_type sequence -i " + temp_img_path +" " + mp4_path
com = software_name + " -y -f image2 -pattern_type sequence -start_number " + str(first_frame) + " -i " + temp_img_path +" " + mp4_path
print("Creating timelapse using ffmpeg...")
else:
print("Creating timelapse using avconv...")
com = "cd " + dir_path + ";" \
+ software_name + " -v quiet -r 30 -y -i " + temp_img_path \
+ software_name + " -v quiet -r 30 -y -start_number " + str(first_frame) + " -i " + temp_img_path \
+ " -vcodec libx264 -pix_fmt yuv420p -crf 25 -movflags faststart -g 15 -vf \"hqdn3d=4:3:6:4.5,lutyuv=y=gammaval(0.97)\" " \
+ mp4_path

Expand Down
Loading