Commit 514c0372 authored by Andrey Filippov's avatar Andrey Filippov

added code to generate complete jpeg files viewable with the web browser

parent 8555bdd3
...@@ -34,10 +34,10 @@ __status__ = "Development" ...@@ -34,10 +34,10 @@ __status__ = "Development"
#import pickle #import pickle
from x393_mem import X393Mem from x393_mem import X393Mem
import x393_axi_control_status import x393_axi_control_status
import x393_sens_cmprs
import x393_utils import x393_utils
#import time import time
import vrlg import vrlg
class X393CmprsAfi(object): class X393CmprsAfi(object):
DRY_MODE= True # True DRY_MODE= True # True
...@@ -82,6 +82,131 @@ class X393CmprsAfi(object): ...@@ -82,6 +82,131 @@ class X393CmprsAfi(object):
vrlg.CMPRS_AFIMUX_STATUS_CNTRL, vrlg.CMPRS_AFIMUX_STATUS_CNTRL,
mode, mode,
seq_num) seq_num)
def afi_mux_get_image_pointer(self,
port_afi,
channel):
"""
Returns image pointer in bytes inside the circbuf. Status autoupdate should be already set up, mode = 0
@param port_afi - AFI port (0/1), currently only 0
@param channel - AFI input channel (0..3) - with 2 AFIs - 0..1 only
@return - displacement to current image pointer in bytes
"""
# print ("status reg = 0x%x"%(((vrlg.CMPRS_AFIMUX_REG_ADDR0, vrlg.CMPRS_AFIMUX_REG_ADDR1)[port_afi]+channel) ))
# print ("status reg data = 0x%x"%(self.x393_axi_tasks.read_status((vrlg.CMPRS_AFIMUX_REG_ADDR0, vrlg.CMPRS_AFIMUX_REG_ADDR1)[port_afi]+channel) ))
if (self.DRY_MODE):
return None
return 32*(self.x393_axi_tasks.read_status((vrlg.CMPRS_AFIMUX_REG_ADDR0, vrlg.CMPRS_AFIMUX_REG_ADDR1)[port_afi]+channel) & 0x3ffffff)
def afi_mux_get_image_meta(self,
port_afi,
channel,
cirbuf_start = 0x27a00000,
circbuf_len = 0x1000000,
verbose = 1):
"""
Returns image metadata (start, length,timestamp) or null
@param port_afi - AFI port (0/1), currently only 0
@param channel - AFI input channel (0..3) - with 2 AFIs - 0..1 only
@return - memory segments (1 or two) with image data, timestamp in numeric and string format
"""
print ("x393_sens_cmprs.GLBL_WINDOW = ", x393_sens_cmprs.GLBL_WINDOW)
if (self.DRY_MODE):
return None
CCAM_MMAP_META = 12 # extra bytes included at the end of each frame (last aligned to 32 bytes)
CCAM_MMAP_META_LENGTH = 4 # displacement to length frame length data from the end of the 32-byte aligned frame slot
CCAM_MMAP_META_USEC = 8 # // (negative) displacement to USEC data - 20 bits (frame timestamp)
CCAM_MMAP_META_SEC = 12 # // (negative) displacement to SEC data - 32 bits (frame timestamp)
# offs_len32 = 0x20 - CCAM_MMAP_META_LENGTH # 0x1c #from last image 32-byte chunk to lower of 3-byte image length (MSB == 0xff)
next_image = self.afi_mux_get_image_pointer(port_afi = port_afi,
channel = channel)
last_image_chunk = next_image - 0x40
if last_image_chunk < 0:
last_image_chunk += circbuf_len
len32 = self.x393_mem.read_mem(cirbuf_start + last_image_chunk + (0x20 - CCAM_MMAP_META_LENGTH))
markerFF = len32 >> 24
if (markerFF != 0xff):
print ("Failed to get 0xff marker at offset 0x%08x - length word = 0x%08x)"%(cirbuf_start + last_image_chunk + (0x20 - CCAM_MMAP_META_LENGTH) + 3,len32))
return None
len32 &= 0xffffff
inserted_bytes = (32 - (((len32 % 32) + CCAM_MMAP_META) % 32)) % 32
img_start = last_image_chunk + 32 - CCAM_MMAP_META - inserted_bytes - len32
if img_start < 0:
img_start += circbuf_len
sec = self.x393_mem.read_mem(cirbuf_start + last_image_chunk + (0x20 - CCAM_MMAP_META_SEC))
usec = self.x393_mem.read_mem(cirbuf_start + last_image_chunk + (0x20 - CCAM_MMAP_META_USEC))
fsec=sec + usec/1000000.0
tstr = time.strftime("%b %d %Y %H:%M:%S", time.gmtime(fsec))
segments = ((cirbuf_start + img_start, len32 ),)
if (img_start + len32) > circbuf_len: # split in two segments
segments = ((cirbuf_start + img_start, circbuf_len - img_start),
(cirbuf_start, len32 - (circbuf_len - img_start)))
result = {"timestamp": fsec,
"timestring": tstr,
"segments":segments}
if verbose >0 :
print ("Inserted bytes after image before meta = 0x%x"%(inserted_bytes))
print ("Image start (relative to cirbuf) = 0x%x"%(img_start))
print ("Image time stamp = %s (%f)"%(tstr, fsec))
for s in segments:
print ("start_address = 0x%x, length = 0x%x"%(s[0],s[1]))
return result
"""
>>> hex (0x3dacb * 32)
'0x7b5960'
>>> hex (0x3dacb * 32 + 0x27a00000)
'0x281b5960'
print time.strftime("%b %d %Y %H:%M:%S", time.gmtime(1442344402.605793))
#define CCAM_MMAP_META 12 // extra bytes included at the end of each frame (last aligned to 32 bytes)
#define CCAM_MMAP_META_LENGTH 4 // displacement to length frame length data from the end of the 32-byte aligned frame slot
#define CCAM_MMAP_META_USEC 8 // (negative) displacement to USEC data - 20 bits (frame timestamp)
#define CCAM_MMAP_META_SEC 12 // (negative) displacement to SEC data - 32 bits (frame timestamp)
**
* @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure
*/
inline struct interframe_params_t* updateIRQ_interframe(void) {
int circbuf_size=get_globalParam (G_CIRCBUFSIZE)>>2;
int alen = JPEG_wp-9; if (alen<0) alen+=circbuf_size;
int jpeg_len=ccam_dma_buf_ptr[alen] & 0xffffff;
set_globalParam(G_FRAME_SIZE,jpeg_len);
int aframe_params=(alen & 0xfffffff8)-
(((jpeg_len + CCAM_MMAP_META + 3) & 0xffffffe0)>>2) /// multiple of 32-byte chunks to subtract
-8; /// size of the storage area to be filled before the frame
if(aframe_params < 0) aframe_params += circbuf_size;
struct interframe_params_t* interframe= (struct interframe_params_t*) &ccam_dma_buf_ptr[aframe_params];
/// should we use memcpy as before here?
interframe->frame_length=jpeg_len;
interframe->signffff=0xffff;
#if ELPHEL_DEBUG_THIS
set_globalParam (0x306,get_globalParam (0x306)+1);
#endif
return interframe;
}
/**
* @brief Fill exif data with the current frame data, save pointer to Exif page in the interframe area
"""
# def read_mem (self,addr,quiet=1):
# '''
# Read 32-bit word from physical memory
# @param addr physical byte address
# @param quiet - reduce output
#self.x393_mem
#0x27a00000
# parameter CMPRS_AFIMUX_REG_ADDR0= 'h18, // Uses 4 locations
# parameter CMPRS_AFIMUX_REG_ADDR1= 'h1c, // Uses 4 locations
def afi_mux_reset (self, def afi_mux_reset (self,
port_afi, port_afi,
......
...@@ -38,6 +38,8 @@ from x393_mem import X393Mem ...@@ -38,6 +38,8 @@ from x393_mem import X393Mem
import x393_axi_control_status import x393_axi_control_status
import x393_utils import x393_utils
#import time #import time
import x393_sens_cmprs
import x393_cmprs_afi
import vrlg import vrlg
STD_QUANT_TBLS = { STD_QUANT_TBLS = {
"Y_landscape":( 16, 11, 10, 16, 24, 40, 51, 61, "Y_landscape":( 16, 11, 10, 16, 24, 40, 51, 61,
...@@ -158,12 +160,16 @@ class X393Jpeg(object): ...@@ -158,12 +160,16 @@ class X393Jpeg(object):
x393_mem=None x393_mem=None
x393_axi_tasks=None #x393X393AxiControlStatus x393_axi_tasks=None #x393X393AxiControlStatus
x393_utils=None x393_utils=None
x393_cmprs_afi = None
verbose=1 verbose=1
def __init__(self, debug_mode=1,dry_mode=True, saveFileName=None): def __init__(self, debug_mode=1,dry_mode=True, saveFileName=None):
self.DEBUG_MODE= debug_mode self.DEBUG_MODE= debug_mode
self.DRY_MODE= dry_mode self.DRY_MODE= dry_mode
self.x393_mem= X393Mem(debug_mode,dry_mode) self.x393_mem= X393Mem(debug_mode,dry_mode)
self.x393_axi_tasks= x393_axi_control_status.X393AxiControlStatus(debug_mode,dry_mode) self.x393_axi_tasks= x393_axi_control_status.X393AxiControlStatus(debug_mode,dry_mode)
self.x393_cmprs_afi = x393_cmprs_afi.X393CmprsAfi(debug_mode,dry_mode)
self.x393_utils= x393_utils.X393Utils(debug_mode,dry_mode, saveFileName) # should not overwrite save file path self.x393_utils= x393_utils.X393Utils(debug_mode,dry_mode, saveFileName) # should not overwrite save file path
try: try:
self.verbose=vrlg.VERBOSE self.verbose=vrlg.VERBOSE
...@@ -564,6 +570,57 @@ class X393Jpeg(object): ...@@ -564,6 +570,57 @@ class X393Jpeg(object):
return {"header":buf, return {"header":buf,
"quantization":qtables["fpga"], "quantization":qtables["fpga"],
"huffman": self.huff_tables[FPGA_HUFFMAN_TABLE]} "huffman": self.huff_tables[FPGA_HUFFMAN_TABLE]}
def jpeg_write(self,
file_path = "/www/pages/img.jpeg",
channel = 0,
y_quality = 100, #80,
c_quality = None,
portrait = False,
color_mode = 0,
byrshift = 0,
verbose = 1):
"""
Create JPEG image from the latest acquired in the camera
@param file_path - camera file system path
@param channel - compressor channel
@param y_quality - 1..100 - quantization quality for Y component
@param c_quality - 1..100 - quantization quality for color components (None - use y_quality)
@param portrait - False - use normal order, True - transpose for portrait mode images
@param color_mode - one of the image formats (jpeg, jp4,)
@param byrshift - Bayer shift
@param verbose - verbose level
"""
jpeg_data = self.jpegheader_create (
y_quality = y_quality,
c_quality = c_quality,
portrait = portrait,
height = x393_sens_cmprs.GLBL_WINDOW["height"],
width = x393_sens_cmprs.GLBL_WINDOW["width"],
color_mode = color_mode,
byrshift = byrshift,
verbose = verbose - 1)
meta = self.x393_cmprs_afi.afi_mux_get_image_meta(
port_afi = 0,
channel = channel,
cirbuf_start = x393_sens_cmprs.GLBL_CIRCBUF_STARTS[channel],
circbuf_len = x393_sens_cmprs.GLBL_CIRCBUF_CHN_SIZE,
verbose = 1)
print ("meta = ",meta)
for s in meta["segments"]:
print ("start_address = 0x%x, length = 0x%x"%(s[0],s[1]))
with open (file_path, "w+b") as bf:
bf.write(jpeg_data["header"])
for s in meta["segments"]:
print ("start_address = 0x%x, length = 0x%x"%(s[0],s[1]))
self.x393_mem._mem_write_to_file (bf = bf,
start_addr = s[0],
length = s[1])
bf.write(bytearray((0xff,0xd9)))
def jpegheader_write (self, def jpegheader_write (self,
file_path = "jpeg", file_path = "jpeg",
y_quality = 80, y_quality = 80,
......
...@@ -155,22 +155,36 @@ class X393Mem(object): ...@@ -155,22 +155,36 @@ class X393Mem(object):
if self.DRY_MODE: if self.DRY_MODE:
print ("Write memory to file is not implemented in non-target mode") print ("Write memory to file is not implemented in non-target mode")
return return
with open(filename, "w+b") as sf: with open(filename, "w+b") as bf:
with open("/dev/mem", "r+b") as f: self._mem_write_to_file (bf = bf,
first_page = start_addr // self.PAGE_SIZE start_addr = start_addr,
last_page = (start_addr + length - 1) // self.PAGE_SIZE length = length)
for page_num in range(first_page, last_page+1):
start_offset = 0 def _mem_write_to_file (self, bf, start_addr, length):
if page_num == first_page: '''
start_offset = start_addr - self.PAGE_SIZE * page_num Save physical memory content to a file
end_offset = self.PAGE_SIZE @param bf - file open in write mode
if page_num == last_page: @param start_addr physical byte start address
end_offset = start_addr + length - self.PAGE_SIZE * page_num @param length - number of bytes to save
page_addr = page_num * self.PAGE_SIZE '''
if (page_addr>=0x80000000): if self.DRY_MODE:
page_addr-= (1<<32) print ("Write memory to file is not implemented in non-target mode")
mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr) return
sf.write(mm[start_offset:end_offset]) with open("/dev/mem", "r+b") as f:
first_page = start_addr // self.PAGE_SIZE
last_page = (start_addr + length - 1) // self.PAGE_SIZE
for page_num in range(first_page, last_page+1):
start_offset = 0
if page_num == first_page:
start_offset = start_addr - self.PAGE_SIZE * page_num
end_offset = self.PAGE_SIZE
if page_num == last_page:
end_offset = start_addr + length - self.PAGE_SIZE * page_num
page_addr = page_num * self.PAGE_SIZE
if (page_addr>=0x80000000):
page_addr-= (1<<32)
mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
bf.write(mm[start_offset:end_offset])
def mem_clear (self, start_addr, length, word32): def mem_clear (self, start_addr, length, word32):
''' '''
......
...@@ -60,6 +60,14 @@ BUFFER_PAGES_NAME = 'buffer_pages' ...@@ -60,6 +60,14 @@ BUFFER_PAGES_NAME = 'buffer_pages'
BUFFER_ADDRESS = None # in bytes BUFFER_ADDRESS = None # in bytes
BUFFER_LEN = None # in bytes BUFFER_LEN = None # in bytes
GLBL_CIRCBUF_CHN_SIZE = None
GLBL_CIRCBUF_STARTS = None
GLBL_CIRCBUF_END = None
GLBL_MEMBRIDGE_START = None
GLBL_MEMBRIDGE_END = None
GLBL_BUFFER_END = None
GLBL_WINDOW = None
class X393SensCmprs(object): class X393SensCmprs(object):
DRY_MODE = True # True DRY_MODE = True # True
DEBUG_MODE = 1 DEBUG_MODE = 1
...@@ -470,6 +478,47 @@ class X393SensCmprs(object): ...@@ -470,6 +478,47 @@ class X393SensCmprs(object):
repet_mode = True, # Normal mode, single trigger - just for debugging TODO: re-assign? repet_mode = True, # Normal mode, single trigger - just for debugging TODO: re-assign?
trig = False) trig = False)
return True return True
def specify_window (self,
window_width = 2592, # 2592
window_height = 1944, # 1944
window_left = 0, # 0
window_top = 0, # 0? 1?
):
global GLBL_WINDOW
GLBL_WINDOW = {"width": window_width,
"height": window_height,
"left": window_left,
"top": window_top}
def specify_phys_memory(self,
circbuf_chn_size= 0x1000000,
verbose = 1):
"""
@param circbuf_chn_size - circular buffer size for each channel, in bytes
"""
global GLBL_CIRCBUF_CHN_SIZE, GLBL_CIRCBUF_STARTS, GLBL_CIRCBUF_END, GLBL_MEMBRIDGE_START, GLBL_MEMBRIDGE_END, GLBL_BUFFER_END
circbuf_start = self.get_circbuf_byte_start()
GLBL_BUFFER_END= self.get_circbuf_byte_end()
GLBL_CIRCBUF_CHN_SIZE = circbuf_chn_size
GLBL_CIRCBUF_STARTS=[]
for i in range(16):
GLBL_CIRCBUF_STARTS.append(circbuf_start + i*circbuf_chn_size)
GLBL_CIRCBUF_END = circbuf_start + 4*GLBL_CIRCBUF_CHN_SIZE
GLBL_MEMBRIDGE_START = GLBL_CIRCBUF_END
GLBL_MEMBRIDGE_END = GLBL_BUFFER_END
if verbose >0 :
print ("compressor system memory buffers:")
print ("circbuf start 0 = 0x%x"%(GLBL_CIRCBUF_STARTS[0]))
print ("circbuf start 1 = 0x%x"%(GLBL_CIRCBUF_STARTS[1]))
print ("circbuf start 2 = 0x%x"%(GLBL_CIRCBUF_STARTS[2]))
print ("circbuf start 3 = 0x%x"%(GLBL_CIRCBUF_STARTS[3]))
print ("circbuf end = 0x%x"%(GLBL_BUFFER_END))
print ("membridge start = 0x%x"%(GLBL_MEMBRIDGE_START))
print ("membridge end = 0x%x"%(GLBL_MEMBRIDGE_END))
print ("membridge size = %d bytes"%(GLBL_MEMBRIDGE_END - GLBL_MEMBRIDGE_START))
print ("memory buffer end = 0x%x"%(GLBL_BUFFER_END))
def setup_all_sensors (self, def setup_all_sensors (self,
setup_membridge = False, setup_membridge = False,
exit_step = None, exit_step = None,
...@@ -532,38 +581,37 @@ class X393SensCmprs(object): ...@@ -532,38 +581,37 @@ class X393SensCmprs(object):
@parame verbose - verbose level @parame verbose - verbose level
@return True if all done, False if exited prematurely by exit_step @return True if all done, False if exited prematurely by exit_step
""" """
global GLBL_CIRCBUF_CHN_SIZE, GLBL_CIRCBUF_STARTS, GLBL_CIRCBUF_END, GLBL_MEMBRIDGE_START, GLBL_MEMBRIDGE_END, GLBL_BUFFER_END, GLBL_WINDOW
# camsync_setup ( # camsync_setup (
# 4'hf ); # sensor_mask); # # 4'hf ); # sensor_mask); #
circbuf_start = self.get_circbuf_byte_start()
mem_end= self.get_circbuf_byte_end()
#circbuf_chn_size self.specify_phys_memory(circbuf_chn_size = circbuf_chn_size)
circbuf_starts=[] self.specify_window (window_width = window_width,
for i in range(16): window_height = window_height,
circbuf_starts.append(circbuf_start + i*circbuf_chn_size) window_left = window_left,
circbuf_end = circbuf_start + 4*circbuf_chn_size window_top = window_top)
membridge_start = circbuf_end
membridge_end = mem_end
#TODO: calculate addresses/lengths #TODO: calculate addresses/lengths
""" """
AFI mux is programmed in 32-byte chunks AFI mux is programmed in 32-byte chunks
""" """
afi_cmprs0_sa = circbuf_starts[0] // 32 afi_cmprs0_sa = GLBL_CIRCBUF_STARTS[0] // 32
afi_cmprs1_sa = circbuf_starts[1] // 32 afi_cmprs1_sa = GLBL_CIRCBUF_STARTS[1] // 32
afi_cmprs2_sa = circbuf_starts[2] // 32 afi_cmprs2_sa = GLBL_CIRCBUF_STARTS[2] // 32
afi_cmprs3_sa = circbuf_starts[3] // 32 afi_cmprs3_sa = GLBL_CIRCBUF_STARTS[3] // 32
afi_cmprs_len = circbuf_chn_size // 32 afi_cmprs_len = GLBL_CIRCBUF_CHN_SIZE // 32
if verbose >0 : if verbose >0 :
print ("compressor system memory buffers:") print ("compressor system memory buffers:")
print ("circbuf start 0 = 0x%x"%(circbuf_starts[0])) print ("circbuf start 0 = 0x%x"%(GLBL_CIRCBUF_STARTS[0]))
print ("circbuf start 1 = 0x%x"%(circbuf_starts[1])) print ("circbuf start 1 = 0x%x"%(GLBL_CIRCBUF_STARTS[1]))
print ("circbuf start 2 = 0x%x"%(circbuf_starts[2])) print ("circbuf start 2 = 0x%x"%(GLBL_CIRCBUF_STARTS[2]))
print ("circbuf start 3 = 0x%x"%(circbuf_starts[3])) print ("circbuf start 3 = 0x%x"%(GLBL_CIRCBUF_STARTS[3]))
print ("circbuf end = 0x%x"%(circbuf_end)) print ("circbuf end = 0x%x"%(GLBL_BUFFER_END))
print ("membridge start = 0x%x"%(membridge_start)) print ("membridge start = 0x%x"%(GLBL_MEMBRIDGE_START))
print ("membridge end = 0x%x"%(membridge_end)) print ("membridge end = 0x%x"%(GLBL_MEMBRIDGE_END))
print ("membridge size = %d bytes"%(membridge_end - membridge_start)) print ("membridge size = %d bytes"%(GLBL_MEMBRIDGE_END - GLBL_MEMBRIDGE_START))
print ("memory buffer end = 0x%x"%(mem_end)) print ("memory buffer end = 0x%x"%(GLBL_BUFFER_END))
self.program_status_debug (3,0) self.program_status_debug (3,0)
if setup_membridge: if setup_membridge:
...@@ -573,8 +621,8 @@ class X393SensCmprs(object): ...@@ -573,8 +621,8 @@ class X393SensCmprs(object):
window_height = window_height, window_height = window_height,
window_left = window_left, window_left = window_left,
window_top = window_top, window_top = window_top,
membridge_start = membridge_start, membridge_start = GLBL_MEMBRIDGE_START,
membridge_end = membridge_end, membridge_end = GLBL_MEMBRIDGE_END,
verbose = verbose) verbose = verbose)
if sensor_mask & 3: # Need power for sens1 and sens 2 if sensor_mask & 3: # Need power for sens1 and sens 2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment