acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
SSTV.cpp (9493B)
1 #include "SSTV.h" 2 #if !defined(RADIOLIB_EXCLUDE_SSTV) 3 4 const SSTVMode_t Scottie1 { 5 .visCode = RADIOLIB_SSTV_SCOTTIE_1, 6 .width = 320, 7 .height = 256, 8 .scanPixelLen = 432, 9 .numTones = 7, 10 .tones = { 11 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 12 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 13 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 14 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }, 15 { .type = tone_t::GENERIC, .len = 9000, .freq = 1200 }, 16 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 17 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 } 18 } 19 }; 20 21 const SSTVMode_t Scottie2 { 22 .visCode = RADIOLIB_SSTV_SCOTTIE_2, 23 .width = 320, 24 .height = 256, 25 .scanPixelLen = 275, 26 .numTones = 7, 27 .tones = { 28 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 29 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 30 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 31 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }, 32 { .type = tone_t::GENERIC, .len = 9000, .freq = 1200 }, 33 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 34 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 } 35 } 36 }; 37 38 const SSTVMode_t ScottieDX { 39 .visCode = RADIOLIB_SSTV_SCOTTIE_DX, 40 .width = 320, 41 .height = 256, 42 .scanPixelLen = 1080, 43 .numTones = 7, 44 .tones = { 45 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 46 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 47 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 48 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }, 49 { .type = tone_t::GENERIC, .len = 9000, .freq = 1200 }, 50 { .type = tone_t::GENERIC, .len = 1500, .freq = 1500 }, 51 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 } 52 } 53 }; 54 55 const SSTVMode_t Martin1 { 56 .visCode = RADIOLIB_SSTV_MARTIN_1, 57 .width = 320, 58 .height = 256, 59 .scanPixelLen = 458, 60 .numTones = 8, 61 .tones = { 62 { .type = tone_t::GENERIC, .len = 4862, .freq = 1200 }, 63 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 64 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 65 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 66 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }, 67 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 68 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 69 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 } 70 } 71 }; 72 73 const SSTVMode_t Martin2 { 74 .visCode = RADIOLIB_SSTV_MARTIN_2, 75 .width = 320, 76 .height = 256, 77 .scanPixelLen = 229, 78 .numTones = 8, 79 .tones = { 80 { .type = tone_t::GENERIC, .len = 4862, .freq = 1200 }, 81 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 82 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 83 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 84 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 }, 85 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 }, 86 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 87 { .type = tone_t::GENERIC, .len = 572, .freq = 1500 } 88 } 89 }; 90 91 const SSTVMode_t Wrasse { 92 .visCode = RADIOLIB_SSTV_WRASSE_SC2_180, 93 .width = 320, 94 .height = 256, 95 .scanPixelLen = 734, 96 .numTones = 5, 97 .tones = { 98 { .type = tone_t::GENERIC, .len = 5523, .freq = 1200 }, 99 { .type = tone_t::GENERIC, .len = 500, .freq = 1500 }, 100 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 101 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 102 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 } 103 } 104 }; 105 106 const SSTVMode_t PasokonP3 { 107 .visCode = RADIOLIB_SSTV_PASOKON_P3, 108 .width = 640, 109 .height = 496, 110 .scanPixelLen = 208, 111 .numTones = 7, 112 .tones = { 113 { .type = tone_t::GENERIC, .len = 5208, .freq = 1200 }, 114 { .type = tone_t::GENERIC, .len = 1042, .freq = 1500 }, 115 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 116 { .type = tone_t::GENERIC, .len = 1042, .freq = 1500 }, 117 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 118 { .type = tone_t::GENERIC, .len = 1042, .freq = 1500 }, 119 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 } 120 } 121 }; 122 123 const SSTVMode_t PasokonP5 { 124 .visCode = RADIOLIB_SSTV_PASOKON_P5, 125 .width = 640, 126 .height = 496, 127 .scanPixelLen = 312, 128 .numTones = 7, 129 .tones = { 130 { .type = tone_t::GENERIC, .len = 7813, .freq = 1200 }, 131 { .type = tone_t::GENERIC, .len = 1563, .freq = 1500 }, 132 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 133 { .type = tone_t::GENERIC, .len = 1563, .freq = 1500 }, 134 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 135 { .type = tone_t::GENERIC, .len = 1563, .freq = 1500 }, 136 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 } 137 } 138 }; 139 140 const SSTVMode_t PasokonP7 { 141 .visCode = RADIOLIB_SSTV_PASOKON_P7, 142 .width = 640, 143 .height = 496, 144 .scanPixelLen = 417, 145 .numTones = 7, 146 .tones = { 147 { .type = tone_t::GENERIC, .len = 10417, .freq = 1200 }, 148 { .type = tone_t::GENERIC, .len = 2083, .freq = 1500 }, 149 { .type = tone_t::SCAN_RED, .len = 0, .freq = 0 }, 150 { .type = tone_t::GENERIC, .len = 2083, .freq = 1500 }, 151 { .type = tone_t::SCAN_GREEN, .len = 0, .freq = 0 }, 152 { .type = tone_t::GENERIC, .len = 2083, .freq = 1500 }, 153 { .type = tone_t::SCAN_BLUE, .len = 0, .freq = 0 } 154 } 155 }; 156 157 SSTVClient::SSTVClient(PhysicalLayer* phy) { 158 _phy = phy; 159 #if !defined(RADIOLIB_EXCLUDE_AFSK) 160 _audio = nullptr; 161 #endif 162 } 163 164 #if !defined(RADIOLIB_EXCLUDE_AFSK) 165 SSTVClient::SSTVClient(AFSKClient* audio) { 166 _phy = audio->_phy; 167 _audio = audio; 168 } 169 #endif 170 171 #if !defined(RADIOLIB_EXCLUDE_AFSK) 172 int16_t SSTVClient::begin(const SSTVMode_t& mode, float correction) { 173 if(_audio == nullptr) { 174 // this initialization method can only be used in AFSK mode 175 return(RADIOLIB_ERR_WRONG_MODEM); 176 } 177 178 return(begin(0, mode, correction)); 179 } 180 #endif 181 182 int16_t SSTVClient::begin(float base, const SSTVMode_t& mode, float correction) { 183 // save mode 184 _mode = mode; 185 186 // apply correction factor to all timings 187 _mode.scanPixelLen *= correction; 188 for(uint8_t i = 0; i < _mode.numTones; i++) { 189 _mode.tones[i].len *= correction; 190 } 191 192 // calculate 24-bit frequency 193 _base = (base * 1000000.0) / _phy->getFreqStep(); 194 195 // configure for direct mode 196 return(_phy->startDirect()); 197 } 198 199 void SSTVClient::idle() { 200 _phy->transmitDirect(); 201 this->tone(RADIOLIB_SSTV_TONE_LEADER); 202 } 203 204 void SSTVClient::sendHeader() { 205 // save first header flag for Scottie modes 206 _firstLine = true; 207 _phy->transmitDirect(); 208 209 // send the first part of header (leader-break-leader) 210 this->tone(RADIOLIB_SSTV_TONE_LEADER, RADIOLIB_SSTV_HEADER_LEADER_LENGTH); 211 this->tone(RADIOLIB_SSTV_TONE_BREAK, RADIOLIB_SSTV_HEADER_BREAK_LENGTH); 212 this->tone(RADIOLIB_SSTV_TONE_LEADER, RADIOLIB_SSTV_HEADER_LEADER_LENGTH); 213 214 // VIS start bit 215 this->tone(RADIOLIB_SSTV_TONE_BREAK, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 216 217 // VIS code 218 uint8_t parityCount = 0; 219 for(uint8_t mask = 0x01; mask < 0x80; mask <<= 1) { 220 if(_mode.visCode & mask) { 221 this->tone(RADIOLIB_SSTV_TONE_VIS_1, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 222 parityCount++; 223 } else { 224 this->tone(RADIOLIB_SSTV_TONE_VIS_0, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 225 } 226 } 227 228 // VIS parity 229 if(parityCount % 2 == 0) { 230 // even parity 231 this->tone(RADIOLIB_SSTV_TONE_VIS_0, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 232 } else { 233 // odd parity 234 this->tone(RADIOLIB_SSTV_TONE_VIS_1, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 235 } 236 237 // VIS stop bit 238 this->tone(RADIOLIB_SSTV_TONE_BREAK, RADIOLIB_SSTV_HEADER_BIT_LENGTH); 239 } 240 241 void SSTVClient::sendLine(uint32_t* imgLine) { 242 // check first line flag in Scottie modes 243 if(_firstLine && ((_mode.visCode == RADIOLIB_SSTV_SCOTTIE_1) || (_mode.visCode == RADIOLIB_SSTV_SCOTTIE_2) || (_mode.visCode == RADIOLIB_SSTV_SCOTTIE_DX))) { 244 _firstLine = false; 245 246 // send start sync tone 247 this->tone(RADIOLIB_SSTV_TONE_BREAK, 9000); 248 } 249 250 // send all tones in sequence 251 for(uint8_t i = 0; i < _mode.numTones; i++) { 252 if((_mode.tones[i].type == tone_t::GENERIC) && (_mode.tones[i].len > 0)) { 253 // sync/porch tones 254 this->tone(_mode.tones[i].freq, _mode.tones[i].len); 255 } else { 256 // scan lines 257 for(uint16_t j = 0; j < _mode.width; j++) { 258 uint32_t color = imgLine[j]; 259 switch(_mode.tones[i].type) { 260 case(tone_t::SCAN_RED): 261 color &= 0x00FF0000; 262 color >>= 16; 263 break; 264 case(tone_t::SCAN_GREEN): 265 color &= 0x0000FF00; 266 color >>= 8; 267 break; 268 case(tone_t::SCAN_BLUE): 269 color &= 0x000000FF; 270 break; 271 case(tone_t::GENERIC): 272 break; 273 } 274 this->tone(RADIOLIB_SSTV_TONE_BRIGHTNESS_MIN + ((float)color * 3.1372549), _mode.scanPixelLen); 275 } 276 } 277 } 278 } 279 280 uint16_t SSTVClient::getPictureHeight() const { 281 return(_mode.height); 282 } 283 284 void SSTVClient::tone(float freq, uint32_t len) { 285 Module* mod = _phy->getMod(); 286 uint32_t start = mod->micros(); 287 #if !defined(RADIOLIB_EXCLUDE_AFSK) 288 if(_audio != nullptr) { 289 _audio->tone(freq, false); 290 } else { 291 _phy->transmitDirect(_base + (freq / _phy->getFreqStep())); 292 } 293 #else 294 _phy->transmitDirect(_base + (freq / _phy->getFreqStep())); 295 #endif 296 while(mod->micros() - start < len) { 297 mod->yield(); 298 } 299 } 300 301 #endif