[SSTV] Added Robot36 and Robot72 modes (#1160)

This commit is contained in:
jgromes 2024-07-13 14:53:57 +01:00
parent d1bfccd612
commit fb049cc3af
3 changed files with 158 additions and 81 deletions

View file

@ -74,6 +74,8 @@ Wrasse KEYWORD1
PasokonP3 KEYWORD1
PasokonP5 KEYWORD1
PasokonP7 KEYWORD1
Robot36 KEYWORD1
Robot72 KEYWORD1
# Bell Modems
Bell101 KEYWORD1

View file

@ -8,13 +8,13 @@ const SSTVMode_t Scottie1 {
.scanPixelLen = 432,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 }
}
};
@ -25,13 +25,13 @@ const SSTVMode_t Scottie2 {
.scanPixelLen = 275,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 }
}
};
@ -42,13 +42,13 @@ const SSTVMode_t ScottieDX {
.scanPixelLen = 1080,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 }
}
};
@ -59,14 +59,14 @@ const SSTVMode_t Martin1 {
.scanPixelLen = 458,
.numTones = 8,
.tones = {
{ .type = tone_t::GENERIC, .len = 4862, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 }
{ .type = tone_t::GENERIC, .len = 4862, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 }
}
};
@ -77,14 +77,14 @@ const SSTVMode_t Martin2 {
.scanPixelLen = 229,
.numTones = 8,
.tones = {
{ .type = tone_t::GENERIC, .len = 4862, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 }
{ .type = tone_t::GENERIC, .len = 4862, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 572, .freq = 1500 }
}
};
@ -95,11 +95,11 @@ const SSTVMode_t Wrasse {
.scanPixelLen = 734,
.numTones = 5,
.tones = {
{ .type = tone_t::GENERIC, .len = 5523, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 500, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 5523, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 500, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 }
}
};
@ -110,13 +110,13 @@ const SSTVMode_t PasokonP3 {
.scanPixelLen = 208,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 5208, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 5208, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1042, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 }
}
};
@ -127,13 +127,13 @@ const SSTVMode_t PasokonP5 {
.scanPixelLen = 312,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 7813, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 7813, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 1563, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 }
}
};
@ -144,13 +144,48 @@ const SSTVMode_t PasokonP7 {
.scanPixelLen = 417,
.numTones = 7,
.tones = {
{ .type = tone_t::GENERIC, .len = 10417, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_RED, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }
{ .type = tone_t::GENERIC, .len = 10417, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 2083, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 }
}
};
const SSTVMode_t Robot36 {
.visCode = RADIOLIB_SSTV_ROBOT_36,
.width = 320,
.height = 240,
.scanPixelLen = 275, // this is the Y-scan length, Cb/Cr are one half
.numTones = 6,
.tones = {
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 3000, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 4500, .freq = 1500 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1900 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 }, // on even lines, this is the Cr component
}
};
const SSTVMode_t Robot72 {
.visCode = RADIOLIB_SSTV_ROBOT_72,
.width = 320,
.height = 240,
.scanPixelLen = 431, // this is the Y-scan length, Cb/Cr are one half
.numTones = 9,
.tones = {
{ .type = tone_t::GENERIC, .len = 9000, .freq = 1200 },
{ .type = tone_t::GENERIC, .len = 3000, .freq = 1500 },
{ .type = tone_t::SCAN_GREEN_Y, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 4500, .freq = 1500 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1900 },
{ .type = tone_t::SCAN_RED_CR, .len = 0, .freq = 0 },
{ .type = tone_t::GENERIC, .len = 4500, .freq = 2300 },
{ .type = tone_t::GENERIC, .len = 1500, .freq = 1500 },
{ .type = tone_t::SCAN_BLUE_CB, .len = 0, .freq = 0 },
}
};
@ -210,8 +245,8 @@ void SSTVClient::idle() {
}
void SSTVClient::sendHeader() {
// save first header flag for Scottie modes
firstLine = true;
// reset line counter
lineCount = 0;
phyLayer->transmitDirect();
// send the first part of header (leader-break-leader)
@ -247,10 +282,8 @@ void SSTVClient::sendHeader() {
}
void SSTVClient::sendLine(const uint32_t* imgLine) {
// check first line flag in Scottie modes
if(firstLine && ((txMode.visCode == RADIOLIB_SSTV_SCOTTIE_1) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_2) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_DX))) {
firstLine = false;
// check first line in Scottie modes
if((lineCount == 0) && ((txMode.visCode == RADIOLIB_SSTV_SCOTTIE_1) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_2) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_DX))) {
// send start sync tone
this->tone(RADIOLIB_SSTV_TONE_BREAK, 9000);
}
@ -258,31 +291,67 @@ void SSTVClient::sendLine(const uint32_t* imgLine) {
// send all tones in sequence
for(uint8_t i = 0; i < txMode.numTones; i++) {
if((txMode.tones[i].type == tone_t::GENERIC) && (txMode.tones[i].len > 0)) {
// Robot36 has different separator tones for even and odd lines
uint32_t freq = txMode.tones[i].freq;
if((txMode.visCode == RADIOLIB_SSTV_ROBOT_36) && (i == 3)) {
freq = (lineCount % 2) ? 2300 : txMode.tones[3].freq;
}
// sync/porch tones
this->tone(txMode.tones[i].freq, txMode.tones[i].len);
this->tone(freq, txMode.tones[i].len);
} else {
// scan lines
for(uint16_t j = 0; j < txMode.width; j++) {
uint32_t color = imgLine[j];
uint32_t len = txMode.scanPixelLen;
// Robot modes work in YCbCr
if((txMode.visCode == RADIOLIB_SSTV_ROBOT_36) || (txMode.visCode == RADIOLIB_SSTV_ROBOT_72)) {
uint8_t r = (color & 0x00FF0000) >> 16;
uint8_t g = (color & 0x0000FF00) >> 8;
uint8_t b = (color & 0x000000FF);
uint8_t y = 16.0 + (0.003906 * ((65.738 * r) + (129.057 * g) + (25.064 * b)));
uint8_t cb = 128.0 + (0.003906 * ((-37.945 * r) + (-74.494 * g) + (112.439 * b)));
uint8_t cr = 128.0 + (0.003906 * ((112.439 * r) + (-94.154 * g) + (-18.285 * b)));
color = ((uint32_t)y << 8);
if(txMode.visCode == RADIOLIB_SSTV_ROBOT_36) {
// odd lines carry Cb, even lines carry Cr
color |= (lineCount % 2) ? cb : cr;
} else {
color |= ((uint32_t)cr << 16) | cb;
}
}
switch(txMode.tones[i].type) {
case(tone_t::SCAN_RED):
case(tone_t::SCAN_RED_CR):
color &= 0x00FF0000;
color >>= 16;
if((txMode.visCode == RADIOLIB_SSTV_ROBOT_36) || (txMode.visCode == RADIOLIB_SSTV_ROBOT_72)) {
len /= 2;
}
break;
case(tone_t::SCAN_GREEN):
case(tone_t::SCAN_GREEN_Y):
color &= 0x0000FF00;
color >>= 8;
break;
case(tone_t::SCAN_BLUE):
case(tone_t::SCAN_BLUE_CB):
color &= 0x000000FF;
if((txMode.visCode == RADIOLIB_SSTV_ROBOT_36) || (txMode.visCode == RADIOLIB_SSTV_ROBOT_72)) {
len /= 2;
}
break;
case(tone_t::GENERIC):
break;
}
this->tone(RADIOLIB_SSTV_TONE_BRIGHTNESS_MIN + ((float)color * 3.1372549), txMode.scanPixelLen);
this->tone(RADIOLIB_SSTV_TONE_BRIGHTNESS_MIN + ((float)color * 3.1372549), len);
}
}
}
// increment line counter (needed for Robot36 mode)
lineCount++;
}
uint16_t SSTVClient::getPictureHeight() const {

View file

@ -21,6 +21,8 @@
#define RADIOLIB_SSTV_PASOKON_P3 113
#define RADIOLIB_SSTV_PASOKON_P5 114
#define RADIOLIB_SSTV_PASOKON_P7 115
#define RADIOLIB_SSTV_ROBOT_36 8
#define RADIOLIB_SSTV_ROBOT_72 12
// SSTV tones in Hz
#define RADIOLIB_SSTV_TONE_LEADER 1900
@ -46,9 +48,9 @@ struct tone_t {
*/
enum {
GENERIC = 0,
SCAN_GREEN,
SCAN_BLUE,
SCAN_RED
SCAN_GREEN_Y,
SCAN_BLUE_CB,
SCAN_RED_CR
} type;
/*!
@ -96,7 +98,7 @@ struct SSTVMode_t {
/*!
\brief Sequence of tones in each transmission line. This is used to create the correct encoding sequence.
*/
tone_t tones[8];
tone_t tones[9];
};
// all currently supported SSTV modes
@ -109,6 +111,8 @@ extern const SSTVMode_t Wrasse;
extern const SSTVMode_t PasokonP3;
extern const SSTVMode_t PasokonP5;
extern const SSTVMode_t PasokonP7;
extern const SSTVMode_t Robot36;
extern const SSTVMode_t Robot72;
/*!
\class SSTVClient
@ -136,7 +140,8 @@ class SSTVClient {
\brief Initialization method for 2-FSK.
\param base Base "0 Hz tone" RF frequency to be used in MHz.
\param mode SSTV mode to be used. Currently supported modes are Scottie1, Scottie2,
ScottieDX, Martin1, Martin2, Wrasse, PasokonP3, PasokonP5 and PasokonP7.
ScottieDX, Martin1, Martin2, Wrasse, PasokonP3, PasokonP5 and PasokonP7,
Robot36 and Robot37.
\returns \ref status_codes
*/
int16_t begin(float base, const SSTVMode_t& mode);
@ -145,7 +150,8 @@ class SSTVClient {
/*!
\brief Initialization method for AFSK.
\param mode SSTV mode to be used. Currently supported modes are Scottie1, Scottie2,
ScottieDX, Martin1, Martin2, Wrasse, PasokonP3, PasokonP5 and PasokonP7.
ScottieDX, Martin1, Martin2, Wrasse, PasokonP3, PasokonP5 and PasokonP7,
Robot36 and Robot37.
\returns \ref status_codes
*/
int16_t begin(const SSTVMode_t& mode);
@ -192,7 +198,7 @@ class SSTVClient {
uint32_t baseFreq = 0;
SSTVMode_t txMode = Scottie1;
bool firstLine = true;
uint32_t lineCount = 0;
void tone(float freq, RadioLibTime_t len = 0);
};