Skip to content

Commit

Permalink
indi_rpicam v 1.3
Browse files Browse the repository at this point in the history
* Also supports V1 camera (omv5647 chip).
* Performance improvements, especially for the V1 and V2 camera.
  • Loading branch information
lboclboc committed Jan 5, 2021
2 parents b1c652d + dc531c8 commit dec2368
Show file tree
Hide file tree
Showing 17 changed files with 302 additions and 67 deletions.
7 changes: 7 additions & 0 deletions debian/indi-rpicam/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
indi-rpicam (1.3) stable; urgency=low

* Also supports V1 camera (omv5647 chip).
* Performance improvements, especially for the V1 and V2 camera.

-- Lars Berntzon <lars.berntzon@cecilia-data.se> Tue, 05 Jan 2021 21:00:00 +0200

indi-rpicam (1.2) stable; urgency=low

* Supports long exposure for imx477 (requires one initial waste frame)
Expand Down
24 changes: 21 additions & 3 deletions indi-rpicam/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.0)

LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/")
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules/")

PROJECT(indi_rpicam CXX C)

set(INDI_RPICAM_VERSION_MAJOR 1)
set(INDI_RPICAM_VERSION_MINOR 2)
set(INDI_RPICAM_VERSION_MINOR 3)

set(CMAKE_PREFIX_PATH /opt/vc)

Expand All @@ -17,7 +17,7 @@ include(GNUInstallDirs)
include(CMakeCommon)

find_package(MMAL REQUIRED)
find_package(INDI REQUIRED)
find_package(INDI COMPONENTS driver REQUIRED)
find_package(CFITSIO REQUIRED)
find_package(Nova REQUIRED)
find_package(USB1 REQUIRED)
Expand Down Expand Up @@ -84,4 +84,22 @@ ELSE()
MESSAGE (STATUS "GTEST not found, not building unit tests")
ENDIF (GTEST_FOUND)

# ARM specific flags
include(FindARM.cmake)
IF (NEON_FOUND)
MESSAGE(STATUS "Neon found with compiler flag : -mfpu=neon -D__NEON__")
SET(CMAKE_C_FLAGS "-mfpu=neon -D__NEON__ -ftree-vectorize ${CMAKE_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "-mfpu=neon -D__NEON__ -ftree-vectorize ${CMAKE_CXX_FLAGS}")
ENDIF (NEON_FOUND)
IF (CORTEXA8_FOUND)
MESSAGE(STATUS "Cortex-A8 Found with compiler flag : -mcpu=cortex-a8")
SET(CMAKE_C_FLAGS "-mcpu=cortex-a8 -fprefetch-loop-arrays ${CMAKE_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "-mcpu=cortex-a8 -fprefetch-loop-arrays ${CMAKE_CXX_FLAGS}")
ENDIF (CORTEXA8_FOUND)
IF (CORTEXA9_FOUND)
MESSAGE(STATUS "Cortex-A9 Found with compiler flag : -mcpu=cortex-a9")
SET(CMAKE_C_FLAGS "-mcpu=cortex-a9 ${CMAKE_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "-mcpu=cortex-a9 ${CMAKE_CXX_FLAGS}")
ENDIF (CORTEXA9_FOUND)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/indi_rpicam.xml CONFIGURATIONS Release DESTINATION ${INDI_DATA_DIR})
79 changes: 79 additions & 0 deletions indi-rpicam/FindARM.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Adapted from:
# https://github.com/torch/torch7/blob/master/lib/TH/cmake/FindARM.cmake

# Check if the processor is an ARM and if Neon instructions are available on the
# machine where the project is compiled.

IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
EXEC_PROGRAM(cat ARGS "/proc/cpuinfo" OUTPUT_VARIABLE CPUINFO)

# Neon instruction can be found on the majority part of modern ARM processor.
STRING(REGEX REPLACE "^.*(neon).*$" "\\1" NEON_THERE ${CPUINFO})
STRING(COMPARE EQUAL "neon" "${NEON_THERE}" NEON_TRUE)
IF (NEON_TRUE)
set(NEON_FOUND true CACHE BOOL "NEON available on host")
ELSE (NEON_TRUE)
set(NEON_FOUND false CACHE BOOL "NEON available on host")
ENDIF (NEON_TRUE)

# On ARMv8, neon is inherit and instead listed as 'asimd' in /proc/cpuinfo.
STRING(REGEX REPLACE "^.*(asimd).*$" "\\1" ASIMD_THERE ${CPUINFO})
STRING(COMPARE EQUAL "asimd" "${ASIMD_THERE}" ASIMD_TRUE)
IF (ASIMD_TRUE)
set(ASIMD_FOUND true CACHE BOOL "ASIMD/NEON available on host")
ELSE (ASIMD_TRUE)
set(ASIMD_FOUND false CACHE BOOL "ASIMD/NEON available on host")
ENDIF (ASIMD_TRUE)

# Find the processor type (for now OMAP3 or OMAP4).
STRING(REGEX REPLACE "^.*(OMAP3).*$" "\\1" OMAP3_THERE ${CPUINFO})
STRING(COMPARE EQUAL "OMAP3" "${OMAP3_THERE}" OMAP3_TRUE)
IF (OMAP3_TRUE)
set(CORTEXA8_FOUND true CACHE BOOL "OMAP3 available on host")
ELSE (OMAP3_TRUE)
set(CORTEXA8_FOUND false CACHE BOOL "OMAP3 available on host")
ENDIF (OMAP3_TRUE)

# Find the processor type (for now OMAP3 or OMAP4).
STRING(REGEX REPLACE "^.*(OMAP4).*$" "\\1" OMAP4_THERE ${CPUINFO})
STRING(COMPARE EQUAL "OMAP4" "${OMAP4_THERE}" OMAP4_TRUE)
IF (OMAP4_TRUE)
set(CORTEXA9_FOUND true CACHE BOOL "OMAP4 available on host")
ELSE (OMAP4_TRUE)
set(CORTEXA9_FOUND false CACHE BOOL "OMAP4 available on host")
ENDIF (OMAP4_TRUE)

ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE
CPUINFO)

# Neon instruction can be found on the majority part of modern ARM processor.
STRING(REGEX REPLACE "^.*(neon).*$" "\\1" NEON_THERE ${CPUINFO})
STRING(COMPARE EQUAL "neon" "${NEON_THERE}" NEON_TRUE)
IF (NEON_TRUE)
set(NEON_FOUND true CACHE BOOL "NEON available on host")
ELSE (NEON_TRUE)
set(NEON_FOUND false CACHE BOOL "NEON available on host")
ENDIF (NEON_TRUE)

ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows")
# TODO
set(CORTEXA8_FOUND false CACHE BOOL "OMAP3 not available on host")
set(CORTEXA9_FOUND false CACHE BOOL "OMAP4 not available on host")
set(NEON_FOUND false CACHE BOOL "NEON not available on host")
ELSE(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CORTEXA8_FOUND false CACHE BOOL "OMAP3 not available on host")
set(CORTEXA9_FOUND false CACHE BOOL "OMAP4 not available on host")
set(NEON_FOUND false CACHE BOOL "NEON not available on host")
ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")

if(NOT NEON_FOUND)
MESSAGE(STATUS "Could not find hardware support for NEON on this machine.")
endif(NOT NEON_FOUND)
if(NOT CORTEXA8_FOUND)
MESSAGE(STATUS "No OMAP3 processor on this machine.")
endif(NOT CORTEXA8_FOUND)
if(NOT CORTEXA9_FOUND)
MESSAGE(STATUS "No OMAP4 processor on this machine.")
endif(NOT CORTEXA9_FOUND)
mark_as_advanced(NEON_FOUND)
2 changes: 1 addition & 1 deletion indi-rpicam/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# indi_rpicam
This is an INDI driver for Raspberry Pi High Quality Camera.
This is an INDI driver for Raspberry Pi High Quality Camera, the V2 camera (imx219 chip) and the V1 camera (omv5647 chip).

## Long exposures
For long exposures to work one must first first grab a waste-frame with the desired exposure time.
Expand Down
1 change: 1 addition & 0 deletions indi-rpicam/TODO
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
- Make sure indi_rpicam does not break building whole indi_3rdparty
- raw10-decoders needs to move up high-bits to bit15 in image buffer.
- Try using encoding MMAL_ENCODING_BAYER_SBGGR12P if that works and is even faster.
- Don't start from x=0 on subframes..
- Exposure time does not seem to affect exposure now. printf(stderr from mmalcamera does not get output anywhere.
Expand Down
12 changes: 11 additions & 1 deletion indi-rpicam/cameracontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ CameraControl::~CameraControl()

void CameraControl::startCapture()
{
LOG_TEST("entered");
if (is_capturing) {
LOG_TEST("camera is already capturing..");
return;
}
camera->connect(MMALCamera::CAPTURE_PORT_NO, encoder.get(), 0); // Connected the capture port to the encoder.

camera->setExposureParameters(gain, shutter_speed);
Expand All @@ -67,6 +72,7 @@ void CameraControl::startCapture()
#endif

camera->startCapture();
is_capturing = true;

start_time = std::chrono::steady_clock::now();
print_first = true;
Expand All @@ -75,10 +81,15 @@ void CameraControl::startCapture()
void CameraControl::stopCapture()
{
LOGF_TEST("total time consumed by buffer processing: %f", buffer_processing_time.count());
if (!is_capturing) {
LOG_TEST("camera is not capturing..");
return;
}
camera->stopCapture();
std::chrono::duration<double> diff = std::chrono::steady_clock::now() - start_time;
LOGF_TEST("exposure stopped after %f s", diff.count());
camera->disconnect();
is_capturing = false;
}

/**
Expand Down Expand Up @@ -131,5 +142,4 @@ void CameraControl::signal_complete()
for(auto p : capture_listeners) {
p->capture_complete();
}
stopCapture();
}
1 change: 1 addition & 0 deletions indi-rpicam/cameracontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class CameraControl : MMALBufferListener
bool print_first {true};
double gain {1};
uint32_t shutter_speed {100000};
bool is_capturing {false};

#ifndef NDEBUG
std::chrono::duration <double> buffer_processing_time {};
Expand Down
21 changes: 17 additions & 4 deletions indi-rpicam/jpegpipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void JpegPipeline::reset()
void JpegPipeline::data_received(uint8_t *data, uint32_t length)
{
uint8_t byte;
for(;length; data++, length--)
while(length > 0)
{
byte = *data;
switch(state)
Expand Down Expand Up @@ -74,7 +74,18 @@ void JpegPipeline::reset()
break;

case State::SKIP_BYTES:
skip_bytes--;
if(skip_bytes <= length)
{
length -= skip_bytes;
data += skip_bytes;
skip_bytes = 0;
}
else
{
length = 0;
data += length;
skip_bytes -= length;
}
if (skip_bytes == 0) {
if (entropy_data_follows) {
state = State::WANT_ENTROPY_DATA;
Expand All @@ -83,7 +94,7 @@ void JpegPipeline::reset()
state = State::WANT_FF;
}
}
break;
continue;

case State::WANT_ENTROPY_DATA:
if (byte == 0xFF) {
Expand Down Expand Up @@ -121,7 +132,7 @@ void JpegPipeline::reset()
state = State::END_OF_JPEG;
break;

case 0xda: // SOS (Start of scan)
case 0xda: // SOS (Start of stream)
case 0xc0: // Baseline DCT
case 0xc4: // Huffman Table
entropy_data_follows = true;
Expand All @@ -139,5 +150,7 @@ void JpegPipeline::reset()
}
break;
}
data++;
length--;
}
}
2 changes: 1 addition & 1 deletion indi-rpicam/jpegpipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class JpegPipeline : public Pipeline
private:
int s1 {0}; // Length field, first byte MSB.
int s2 {0}; // Length field, second byte LSB.
int skip_bytes {0}; //Counter for skipping bytes.
uint32_t skip_bytes {0}; //Counter for skipping bytes.
bool entropy_data_follows {false};
int current_type {}; // For debugging only.
};
Expand Down
3 changes: 3 additions & 0 deletions indi-rpicam/mmalcamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ void MMALCamera::getSensorInfo()
if (!strcmp(cameraModel, "imx477")) {
xPixelSize = yPixelSize = 1.55F;
}
else if (!strcmp(cameraModel, "ov5647")) {
xPixelSize = yPixelSize = 1.4F;
}
else if (!strcmp(cameraModel, "imx219")) {
xPixelSize = yPixelSize = 1.12F;
}
Expand Down
12 changes: 3 additions & 9 deletions indi-rpicam/mmalcomponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,9 @@ void MMALComponent::port_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffe
{
mmal_buffer_header_mem_lock(buffer);

try {
for(auto *l : buffer_listeners)
{
l->buffer_received(port, buffer);
}
}
catch (std::exception &e) {
mmal_buffer_header_mem_unlock(buffer);
throw e;
for(auto *l : buffer_listeners)
{
l->buffer_received(port, buffer);
}

mmal_buffer_header_mem_unlock(buffer);
Expand Down
29 changes: 20 additions & 9 deletions indi-rpicam/mmaldriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,6 @@ bool MMALDriver::Connect()

camera_control.reset(new CameraControl());

// // FIXME: Seems the HIQ-camera is quite buggy, it needs the mmal_component to be opened twice
// camera_control.reset(); // Since the reset below would allocate the second camera object before the first got deleted.
// camera_control.reset(new CameraControl());

camera_control->add_capture_listener(this);

setupPipeline();
Expand All @@ -157,9 +153,8 @@ bool MMALDriver::Connect()
camera_control->get_camera()->xPixelSize,
camera_control->get_camera()->yPixelSize);

// // Should probably not be called by the subclass of CCD - not clear.
// UpdateCCDFrame(0, 0, static_cast<int>(camera_control->get_camera()->get_width()),
// static_cast<int>(camera_control->get_camera()->get_height()));
uint32_t nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8;
PrimaryCCD.setFrameBufferSize(nbuf);

return true;
}
Expand Down Expand Up @@ -209,8 +204,7 @@ bool MMALDriver::initProperties()
IUFillSwitch(&mIsoS[1], "ISO_200", "200", ISS_OFF);
IUFillSwitch(&mIsoS[2], "ISO_400", "400", ISS_ON);
IUFillSwitch(&mIsoS[3], "ISO_800", "800", ISS_OFF);
IUFillSwitchVector(&mIsoSP, mIsoS, 4, getDeviceName(), "CCD_ISO", "ISO", IMAGE_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60,
IPS_IDLE);
IUFillSwitchVector(&mIsoSP, mIsoS, 4, getDeviceName(), "CCD_ISO", "ISO", IMAGE_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
#endif

// CCD Gain
Expand Down Expand Up @@ -312,6 +306,8 @@ bool MMALDriver::UpdateCCDFrame(int x, int y, int w, int h)
bool MMALDriver::StartExposure(float duration)
{
LOGF_DEBUG("%s(%f)", __FUNCTION__, duration);
assert(PrimaryCCD.getFrameBuffer() != 0);


if (InExposure)
{
Expand Down Expand Up @@ -448,6 +444,9 @@ void MMALDriver::TimerHit()
ccdBufferLock.unlock();
InExposure = false;

// Stop capturing (must be done from main thread).
camera_control->stopCapture();

// Let INDI::CCD know we're done filling the image buffer
LOG_DEBUG("Exposure complete.");
ExposureComplete(&PrimaryCCD);
Expand Down Expand Up @@ -567,6 +566,18 @@ void MMALDriver::setupPipeline()
Raw12ToBayer16Pipeline *raw12_pipe = new Raw12ToBayer16Pipeline(brcm_pipe, &chipWrapper);
brcm_pipe->daisyChain(raw12_pipe);
}
else if (!strcmp(camera_control->get_camera()->getModel(), "ov5647")) {


raw_pipe.reset(new JpegPipeline());

BroadcomPipeline *brcm_pipe = new BroadcomPipeline();
raw_pipe->daisyChain(brcm_pipe);

Raw10ToBayer16Pipeline *raw10_pipe = new Raw10ToBayer16Pipeline(brcm_pipe, &chipWrapper);
// receiver->daisyChain(&raw_writer);
brcm_pipe->daisyChain(raw10_pipe);
}
else if (!strcmp(camera_control->get_camera()->getModel(), "imx219"))
{
raw_pipe.reset(new JpegPipeline());
Expand Down
Loading

0 comments on commit dec2368

Please sign in to comment.