前两天我成功用电脑上的gnuradio流图发射了robot8格式的信号,并且用portapack解调出了图片,但是这样还是不太方便,我在想用Portapack发射robot8的格式,用另一个来收。
但是目前portapack的sstv发射不支持robot8制式。
所以我想到基于它修改,分为2步,先是要确保portapack能从tf卡上读到黑白的bmp图片。
第一步很容易搞定,只要让它不要读3个数字作为一个像素点RGB,而是每个值都是一个像素点亮度就行。只要改firmware/application/app/ui_sstvtx.cpp就行。
void SSTVTXView::paint(Painter&) {
ui::Color line_buffer[240];
Coord line;
uint32_t data_idx, bmp_px, pixel_idx;
data_idx = bmp_header.image_data;
for (line = 0; line < 140; line++) {
// Buffer a whole line
for (bmp_px = 0; bmp_px < 160; bmp_px++) {
line_buffer[bmp_px] = Color(0,0,0);
}
portapack::display.render_line({ 16, 95 + 128 - line }, 160, line_buffer);
}
if (options_grey.value() == 0) {
for (line = 0; line < (256 / 2); line++) {
// Buffer a whole line
read_boundary(pixels_buffer, data_idx, sizeof(pixels_buffer));
for (bmp_px = 0; bmp_px < 160; bmp_px++) {
pixel_idx = bmp_px * 3 * 2;
line_buffer[bmp_px] = Color(pixels_buffer[pixel_idx + 2],
pixels_buffer[pixel_idx + 1],
pixels_buffer[pixel_idx + 0]);
}
portapack::display.render_line({ 16, 95 + 128 - line }, 160, line_buffer);
data_idx += sizeof(pixels_buffer) * 2;
}
}
else if (options_grey.value() == 1){
for (line = 0; line < 140; line=line+2) {
// Buffer a whole line
read_boundary(pixels_buffer_grey, data_idx, sizeof(pixels_buffer_grey));
for (bmp_px = 0; bmp_px < 140; bmp_px++) {
pixel_idx = bmp_px * 2; //both *1 or *2 would work
line_buffer[bmp_px] = spectrum_rgb4_lut[pixels_buffer_grey[pixel_idx]];
}
portapack::display.render_line({ 16, 95 + 128 - line }, 140, line_buffer);
portapack::display.render_line({ 16, 95 + 127 - line }, 140, line_buffer);
data_idx += sizeof(pixels_buffer_grey) * 2; //both *1 or *2 would work
}
}
}
第二步,我又分为2小步。(firmware/application/app/ui_sstvtx.cpp firmware/baseband/proc_sstvtx.cpp firmware/common/sstv.hpp)
先是用martin m1发射彩色图片,我发现这时候portapack也能收到图像,把rgb中的一个颜色通道当作黑白值显示出来了。
接下来我慢慢把martin m1改为robot8的参数,每改动一步观察一下图像是否还能在portapack接收端成功解出来。
ui_sstvtx.cpp
void SSTVTXView::prepare_scanline() {
sstv_scanline scanline_buffer;
uint32_t component, pixel_idx;
uint8_t offset;
if (options_grey.value() == 0) {
if (scanline_counter >= (256 * 3)) {
progressbar.set_value(0);
transmitter_model.disable();
options_bitmaps.set_focusable(true);
tx_view.set_transmitting(false);
return;
}
progressbar.set_value(scanline_counter);
component = scanline_counter % 3;
if ((!scanline_counter && tx_sstv_mode->sync_on_first) || (component == tx_sstv_mode->sync_index)) {
// Sync
scanline_buffer.start_tone.frequency = SSTV_F2D(1200);
scanline_buffer.start_tone.duration = tx_sstv_mode->samples_per_sync;
scanline_buffer.gap_tone.frequency = SSTV_F2D(1500);
scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap;
} else {
// Regular scanline
scanline_buffer.start_tone.duration = 0;
if (tx_sstv_mode->gaps) {
scanline_buffer.gap_tone.frequency = SSTV_F2D(1500);
scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap;
}
}
if (!component) {
// Read a new line
read_boundary(pixels_buffer,
bmp_header.image_data + ((255 - (scanline_counter / 3)) * sizeof(pixels_buffer)),
sizeof(pixels_buffer));
}
offset = component_map[component];
for (uint32_t bmp_px = 0; bmp_px < 320; bmp_px++) {
pixel_idx = bmp_px * 3;
scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx + offset];
}
baseband::set_fifo_data((int8_t *)&scanline_buffer);
scanline_counter++;
}
else if (options_grey.value() == 1)
{
if (scanline_counter >= 140) {
progressbar.set_value(0);
transmitter_model.disable();
options_bitmaps.set_focusable(true);
tx_view.set_transmitting(false);
return;
}
progressbar.set_value(scanline_counter);
component = scanline_counter % 1;
if (component == tx_sstv_mode->sync_index) {
// Sync
scanline_buffer.start_tone.frequency = SSTV_F2D(1200);
scanline_buffer.start_tone.duration = tx_sstv_mode->samples_per_sync;
scanline_buffer.gap_tone.frequency = SSTV_F2D(1500);
scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap;
} else {
// Regular scanline
scanline_buffer.start_tone.duration = 0;
if (tx_sstv_mode->gaps) {
scanline_buffer.gap_tone.frequency = SSTV_F2D(1500);
scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap;
}
}
if (!component) {
// Read a new line
read_boundary(pixels_buffer_grey,
bmp_header.image_data + ((139 - (scanline_counter / 1)) * sizeof(pixels_buffer_grey)),
sizeof(pixels_buffer_grey));
}
for (uint32_t bmp_px = 0; bmp_px < 140; bmp_px++) {
pixel_idx = bmp_px * 2;
scanline_buffer.luma[bmp_px] = pixels_buffer_grey[pixel_idx];
}
baseband::set_fifo_data((int8_t *)&scanline_buffer);
scanline_counter++;
}
}
proc_sstvtx.cpp
else if (state == STATE_PIXELS) {
// Many times per scanline
tone_delta = SSTV_F2D(1500 + ((current_scanline->luma[pixel_index] * 800) / 256));
sample_count = pixel_duration;
pixel_index++;
//the problem is casued here, robot8 vis_code!=2!!!!!!!
if (vis_code == sstv_parity(02)) {
if (pixel_index >= 140) {
// Scanline done, (dirty) state jump
pixel_index = 0;
state = STATE_VIS;
substep = 10;
}
}
else {
if (pixel_index >= 320) {
// Scanline done, (dirty) state jump
pixel_index = 0;
state = STATE_VIS;
substep = 10;
}
}
}
sstv.hpp
{ "Robot 8", sstv_parity(02), false, SSTV_BW, 140, 140, SSTV_MS2S(0.4372), false, 0, true, SSTV_MS2S(4.862), SSTV_MS2S(0.572) },
演示视频:
https://www.bilibili.com/video/BV1ZA411x7QN/
一般我们手头的都是彩色的jpg大图片,要自己制作图片步骤如下:
1.先用 office picture manager调整图片尺寸为140x140像素。
2.再用win下画图软件把图中内容剪切到左边2/3处,因为右边1/3到时会被忽略掉。
3.然后运行下面的程序,把彩色转为灰度图(不能用画图软件转为单色图)。
rgb_to_grey.py
import cv2
def main():
img_src = cv2.imread("140140.jpg")
img_grey = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
print("img_src:", img_src.shape)
print("img_grey:", img_grey.shape)
cv2.imshow("img_src", img_src)
cv2.imshow("img_grey", img_grey)
cv2.imwrite("140140grey.jpg",img_grey)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
4.最后用ubuntu下默认的那个看图软件保存为bmp格式。
下面是我发射的图片例子。