Improved image over USB implementation, created python decoder script

Develop
Sven Steudte 2017-09-08 16:28:01 +02:00
rodzic 08e798ce5e
commit b7fcbc5406
11 zmienionych plików z 512 dodań i 407 usunięć

Wyświetl plik

@ -0,0 +1,86 @@
#!/usr/bin/python
import serial,os,re,sys
import pygame
from pygame.locals import *
import pygame.time
from cStringIO import StringIO
send_to_server = False
SCREENX = 640
SCREENY = 480
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 20)
textsurface = myfont.render('Callsign: DL7AD2 Image ID: 07 Resolution: 640x480', False, (0, 255, 255))
pygame.init()
screen = pygame.display.set_mode((SCREENX, SCREENY))
background = pygame.Surface(screen.get_rect().size)
displaygroup = pygame.sprite.RenderUpdates()
updategroup = pygame.sprite.Group()
clock = pygame.time.Clock()
pygame.display.set_caption('PecanRXGui v.1.0.0 (Q)uit (s)end image')
try:
ser = serial.Serial(port='/dev/ttyACM1')
except:
sys.stderr.write('Error: Could not open serial port\n')
sys.exit(1)
ser.write('picture\r\n')
i=0
while True:
line = ser.readline()
m = re.search("\[(.*)\]\[(.*)\] DATA \> image\/jpeg\,(.*)", line)
try:
size = m.group(3)
except:
print line.strip()
continue
imgbuf = ser.read(int(size))
f = open('data'+str(i)+'.jpg', 'wb')
f.write(imgbuf)
f.close()
i+=1
for event in pygame.event.get():
if event.type == QUIT:
exit(0)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
exit(0)
elif event.key == pygame.K_s:
send_to_server^=True
displaygroup.clear(screen, background)
updategroup.update()
try:
img=pygame.image.load(StringIO(imgbuf))
textsurface = myfont.render("Call: %s send: %d" % ('USB', send_to_server), False, (0, 255, 255))
screen.blit(img,(0,0))
screen.blit(textsurface,(0,0))
pygame.display.flip()
pygame.display.update(displaygroup.draw(screen))
except Exception as e:
print str(e)
textsurface = myfont.render('Error %s' % (e), False, (255, 100, 100))
screen.blit(textsurface,(0,0))
pygame.display.flip()
pygame.display.update(displaygroup.draw(screen))
ser.write('picture\r\n')

Wyświetl plik

@ -463,7 +463,7 @@ void start_user_modules(void)
config[4].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[4].ssdv_conf.res = RES_VGA; // Resolution VGA
config[4].ssdv_conf.quality = 4; // Image quality
start_image_thread(&config[4]);
//start_image_thread(&config[4]);
// Module IMAGE, SSDV 2m 2FSK
config[5].power = 127; // Transmission Power
@ -483,32 +483,22 @@ void start_user_modules(void)
config[5].ssdv_conf.quality = 4; // Image quality
//start_image_thread(&config[5]);
// Module IMAGE, USB
config[6].protocol = PROT_SSDV_USB; // Protocol SSDV transmission over USB
config[6].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
chsnprintf(config[6].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign
config[6].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[6].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[6].ssdv_conf.res = RES_XGA; // Resolution XGA
config[6].ssdv_conf.quality = 4; // Image quality
//start_image_thread(&config[6]);
/* ----------------------------------------------------- LOG TRANSMISSION ---------------------------------------------------- */
// Module LOG, APRS 2m AFSK
config[7].power = 127; // Transmission Power
config[7].protocol = PROT_APRS_AFSK; // Protocol APRS (AFSK)
config[7].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[7].frequency.hz = 144800000; // Default frequency 144.800 MHz
config[7].init_delay = 60000; // Module startup delay (60 seconds)
config[7].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 180 seconds)
config[7].trigger.timeout = 180; // Timeout 180 sec
chsnprintf(config[7].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[7].aprs_conf.ssid = 12; // APRS SSID
chsnprintf(config[7].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[7].aprs_conf.preamble = 300; // APRS Preamble (300ms)
//start_logging_thread(&config[7]);
config[6].power = 127; // Transmission Power
config[6].protocol = PROT_APRS_AFSK; // Protocol APRS (AFSK)
config[6].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[6].frequency.hz = 144800000; // Default frequency 144.800 MHz
config[6].init_delay = 60000; // Module startup delay (60 seconds)
config[6].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 180 seconds)
config[6].trigger.timeout = 180; // Timeout 180 sec
chsnprintf(config[6].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[6].aprs_conf.ssid = 12; // APRS SSID
chsnprintf(config[6].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[6].aprs_conf.preamble = 300; // APRS Preamble (300ms)
//start_logging_thread(&config[6]);
}

Wyświetl plik

@ -19,7 +19,6 @@
* too when operating at 3V. This option will also run the STM32 at 48MHz (AHB) permanently
* because USB needs that speed, otherwise it is running at 6MHz which saves a lot of power. */
#include "ch.h"
#include "types.h"
#include "radio.h"

Wyświetl plik

@ -3,6 +3,7 @@
#include "debug.h"
#include <stdlib.h>
#include "config.h"
#include "image.h"
const SerialConfig uart_config =
{
@ -32,6 +33,48 @@ void debugOnUSB_On(BaseSequentialStream *chp, int argc, char *argv[])
debug_on_usb = true;
}
static uint8_t usb_buffer[128*1024] __attribute__((aligned(32))); // USB image buffer
void printPicture(BaseSequentialStream *chp, int argc, char *argv[])
{
(void)chp;
(void)argc;
(void)argv;
// Take picture
ssdv_conf_t conf = {
.res = RES_VGA,
.quality = 4,
.ram_buffer = usb_buffer,
.ram_size = sizeof(usb_buffer),
};
bool camera_found = takePicture(&conf, false);
// Transmit image via USB
if(camera_found)
{
bool start_detected = false;
for(uint32_t i=0; i<conf.size_sampled; i++)
{
// Look for APP0 instead of SOI because SOI is lost sometimes, but we can add SOI easily later on
if(!start_detected && conf.ram_buffer[i] == 0xFF && conf.ram_buffer[i+1] == 0xE0) {
start_detected = true;
TRACE_USB("DATA > image/jpeg,%d", conf.size_sampled-i+1); // Flag the data on serial output
streamPut(&SDU1, 0xFF);
streamPut(&SDU1, 0xD8);
}
if(start_detected)
streamPut(&SDU1, conf.ram_buffer[i]);
}
} else { // No camera found
for(uint32_t i=0; i<sizeof(noCameraFound); i++)
streamPut(&SDU1, noCameraFound[i]);
}
}
void printConfig(BaseSequentialStream *chp, int argc, char *argv[])
{
if(argc < 1)

Wyświetl plik

@ -106,6 +106,7 @@ extern bool debug_on_usb;
void debugOnUSB_Off(BaseSequentialStream *chp, int argc, char *argv[]);
void debugOnUSB_On(BaseSequentialStream *chp, int argc, char *argv[]);
void printConfig(BaseSequentialStream *chp, int argc, char *argv[]);
void printPicture(BaseSequentialStream *chp, int argc, char *argv[]);
#endif

Wyświetl plik

@ -430,7 +430,7 @@ static const struct regval_list OV5640_JPEG_QSXGA[] =
{0x3824 ,0x04},
{0x5001 ,0x83},
{0x3036 ,0x69},
{0x3035 ,0x11},
{0x3035 ,0x12},
{0x4005 ,0x1A},
{0xffff, 0xff},
};
@ -799,6 +799,7 @@ void set6MHz(void)
*/
static bool analyze_image(uint8_t *image, uint32_t image_len)
{
return true;
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE];
uint8_t *b;
@ -843,11 +844,12 @@ bool OV5640_BufferOverflow(void)
/**
* Captures an image from the camera.
*/
bool OV5640_Snapshot2RAM(void)
bool OV5640_Snapshot2RAM(bool enableJpegValidation)
{
// Capture image until we get a good image (max 10 tries)
uint8_t cntr = 10;
bool status;
bool jpegValid;
do {
TRACE_INFO("CAM > Capture image");
@ -860,16 +862,12 @@ bool OV5640_Snapshot2RAM(void)
TRACE_INFO("CAM > Image size: %d bytes", ov5640_conf->size_sampled);
} while((!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) || !status) && cntr--);
jpegValid = enableJpegValidation ? true : analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size);
} while((!jpegValid || !status) && cntr--);
return true;
}
uint32_t OV5640_getBuffer(uint8_t** buffer) {
*buffer = ov5640_conf->ram_buffer;
return ov5640_conf->size_sampled;
}
const stm32_dma_stream_t *dmastp;
#if OV5640_USE_DMA_DBM == TRUE
@ -1256,7 +1254,7 @@ bool OV5640_Capture(void)
do { // Have a look for some bytes in memory for testing if capturing works
TRACE_INFO("CAM > ... capturing");
chThdSleepMilliseconds(200);
chThdSleepMilliseconds(50);
} while(!capture_finished && !dma_error);
if (dma_error) {

Wyświetl plik

@ -11,10 +11,9 @@
#define OV5640_USE_DMA_DBM TRUE
bool OV5640_Snapshot2RAM(void);
bool OV5640_Snapshot2RAM(bool enableJpegValidation);
bool OV5640_Capture(void);
void OV5640_InitGPIO(void);
uint32_t OV5640_getBuffer(uint8_t** buffer);
bool OV5640_BufferOverflow(void);
void OV5640_TransmitConfig(void);
void OV5640_init(ssdv_conf_t *config);

Wyświetl plik

@ -10,6 +10,7 @@
static const ShellCommand commands[] = {
{"dbgon", debugOnUSB_On},
{"dbgoff", debugOnUSB_Off},
{"picture", printPicture},
// {"printconfig", printConfig}, FIXME: This feature is faulty at the moment
{NULL, NULL}
};

Wyświetl plik

@ -15,7 +15,7 @@
#include "watchdog.h"
#include "flash.h"
const uint8_t noCameraFound[] = {
const uint8_t noCameraFound[4071] = {
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48,
0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10,
0x0E, 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23,
@ -297,8 +297,6 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
msg.freq = getFrequency(&conf->frequency);
msg.power = conf->power;
//image_id = 0; // FIXME temporary
ax25_t ax25_handle;
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
@ -376,8 +374,6 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
// Initialize new packet buffer
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
msg.bin_len = 0;
//chThdSleepMilliseconds(8000); // FIXME: Throttle it for my poor TH-D72. Has to be removed later.
}
break;
@ -405,14 +401,6 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
}
break;
case PROT_SSDV_USB:
// Encode packet
TRACE_INFO("IMG > Encode 2FSK/SSDV packet");
base91_encode(&pkt[1], pkt_base91, sizeof(pkt)-37); // Sync byte, CRC and FEC of SSDV not transmitted
TRACE_USB("DATA > %s", pkt_base91);
break;
default:
TRACE_ERROR("IMG > Unsupported protocol selected for module IMAGE");
}
@ -425,73 +413,62 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
}
}
THD_FUNCTION(imgThread, arg) {
module_conf_t* conf = (module_conf_t*)arg;
bool camInitialized = false;
static bool camInitialized = false;
systime_t time = chVTGetSystemTimeX();
while(true)
bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
{
TRACE_INFO("IMG > Do module IMAGE cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
bool camera_found = false;
if(!p_sleep(&conf->sleep_conf))
{
uint32_t image_len = 0;
uint8_t *image;
// Lock camera FIXME: Removed for testing
// Lock camera
TRACE_INFO("IMG > Lock camera");
chMtxLock(&camera_mtx);
uint8_t tries;
bool status = false;
bool camera_found = false;
// Detect camera
if(camInitialized || OV5640_isAvailable()) { // OV5640 available
TRACE_INFO("IMG > OV5640 found");
camera_found = true;
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
if(conf->res == RES_MAX) // Attempt maximum resolution (limited by memory)
{
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
conf->res = RES_UXGA; // Try maximum resolution
do {
// Init camera
if(!camInitialized) {
OV5640_init(&conf->ssdv_conf);
OV5640_init(conf);
camInitialized = true;
}
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
uint8_t tries = 5; // Try 5 times at maximum
bool status;
do { // Try capturing image until capture successful
lockRadio(); // Lock radio
status = OV5640_Snapshot2RAM();
status = OV5640_Snapshot2RAM(enableJpegValidation);
unlockRadio(); // Unlock radio
} while(!status && --tries);
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
conf->res--; // Decrement resolution in next attempt (if status==false)
} while(OV5640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
} while(OV5640_BufferOverflow() && conf->res >= RES_QVGA);
conf->ssdv_conf.res = RES_MAX; // Revert register
conf->res = RES_MAX; // Revert register
} else { // Static resolution
// Init camera
if(!camInitialized) {
OV5640_init(&conf->ssdv_conf);
OV5640_init(conf);
camInitialized = true;
}
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
uint8_t tries = 5; // Try 5 times at maximum
bool status;
do { // Try capturing image until capture successful
status = OV5640_Snapshot2RAM();
status = OV5640_Snapshot2RAM(enableJpegValidation);
} while(!status && --tries);
}
@ -502,10 +479,6 @@ THD_FUNCTION(imgThread, arg) {
camInitialized = false;
}
// Get image
image_len = OV5640_getBuffer(&image);
TRACE_INFO("IMG > Image size: %d bytes", image_len);
} else { // Camera error
camInitialized = false;
@ -513,23 +486,35 @@ THD_FUNCTION(imgThread, arg) {
}
// Unlock camera FIXME: Removed for testing
// Unlock camera
TRACE_INFO("IMG > Unlock camera");
chMtxUnlock(&camera_mtx);
// Encode/Transmit SSDV if image sampled successfully
if(status) {
return camera_found;
}
gimage_id++;
THD_FUNCTION(imgThread, arg) {
module_conf_t* conf = (module_conf_t*)arg;
systime_t time = chVTGetSystemTimeX();
while(true)
{
TRACE_INFO("IMG > Do module IMAGE cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
if(!p_sleep(&conf->sleep_conf))
{
// Take picture
bool camera_found = takePicture(&conf->ssdv_conf, true);
gimage_id++; // Increase SSDV image counter
// Radio transmission
if(camera_found) {
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
} else if(!camera_found) { // No camera found
gimage_id++;
encode_ssdv(conf->ssdv_conf.ram_buffer, conf->ssdv_conf.size_sampled, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
} else { // No camera found
TRACE_INFO("IMG > Encode/Transmit SSDV (no cam found) ID=%d", gimage_id-1);
encode_ssdv(noCameraFound, sizeof(noCameraFound), conf, gimage_id-1, conf->ssdv_conf.redundantTx);
}
}

Wyświetl plik

@ -7,7 +7,10 @@
#define IMG_ID_FLASH_ADDR 0x80A0000 /* Image ID flash memory address */
#define IMG_ID_FLASH_SIZE (128*1024) /* Image ID flash memory size */
extern const uint8_t noCameraFound[4071];
void start_image_thread(module_conf_t *conf);
bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation);
extern mutex_t camera_mtx;
#endif

Wyświetl plik

@ -14,8 +14,8 @@ typedef enum { // Modulation type
// Protocol type
typedef enum {
PROT_NOT_SET,
PROT_SSDV_2FSK,
PROT_SSDV_USB,
PROT_APRS_AFSK,
PROT_APRS_2GFSK,
PROT_UKHAS_2FSK,