[SSTV] Added AFSK support
This commit is contained in:
parent
f7f81cd41e
commit
0a07f22e93
3 changed files with 209 additions and 6 deletions
155
examples/SSTV/SSTV_Transmit_AFSK/SSTV_Transmit_AFSK.ino
Normal file
155
examples/SSTV/SSTV_Transmit_AFSK/SSTV_Transmit_AFSK.ino
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
RadioLib SSTV Transmit AFSK Example
|
||||||
|
|
||||||
|
The following example sends SSTV picture using
|
||||||
|
SX1278's FSK modem. The data is modulated
|
||||||
|
as AFSK.
|
||||||
|
|
||||||
|
Other modules that can be used for SSTV:
|
||||||
|
with AFSK modulation:
|
||||||
|
- SX127x/RFM9x
|
||||||
|
- RF69
|
||||||
|
- SX1231
|
||||||
|
- CC1101
|
||||||
|
- Si443x/RFM2x
|
||||||
|
|
||||||
|
NOTE: Some platforms (such as Arduino Uno)
|
||||||
|
might not be fast enough to correctly
|
||||||
|
send pictures via high-speed modes
|
||||||
|
like Scottie2 or Martin2. For those,
|
||||||
|
lower speed modes such as Wrasse,
|
||||||
|
Scottie1 or Martin1 are recommended.
|
||||||
|
|
||||||
|
For full API reference, see the GitHub Pages
|
||||||
|
https://jgromes.github.io/RadioLib/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// include the library
|
||||||
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
// SX1278 has the following connections:
|
||||||
|
// NSS pin: 10
|
||||||
|
// DIO0 pin: 2
|
||||||
|
// RESET pin: 9
|
||||||
|
// DIO1 pin: 3
|
||||||
|
SX1278 fsk = new Module(10, 2, 9, 3);
|
||||||
|
|
||||||
|
// or using RadioShield
|
||||||
|
// https://github.com/jgromes/RadioShield
|
||||||
|
//SX1278 fsk = RadioShield.ModuleA;
|
||||||
|
|
||||||
|
// create AFSK client instance using the FSK module
|
||||||
|
// pin 5 is connected to SX1278 DIO2
|
||||||
|
AFSKClient audio(&fsk, 5);
|
||||||
|
|
||||||
|
// create SSTV client instance using the AFSK instance
|
||||||
|
SSTVClient sstv(&audio);
|
||||||
|
|
||||||
|
// test "image" - actually just a single 320px line
|
||||||
|
// will be sent over and over again, to create vertical color stripes at the receiver
|
||||||
|
uint32_t line[320] = {
|
||||||
|
// black
|
||||||
|
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
|
||||||
|
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
|
||||||
|
|
||||||
|
// blue
|
||||||
|
0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF,
|
||||||
|
0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF, 0x0000FF,
|
||||||
|
|
||||||
|
// green
|
||||||
|
0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00,
|
||||||
|
0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FF00,
|
||||||
|
|
||||||
|
// cyan
|
||||||
|
0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF,
|
||||||
|
0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF, 0x00FFFF,
|
||||||
|
|
||||||
|
// red
|
||||||
|
0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000,
|
||||||
|
0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000,
|
||||||
|
|
||||||
|
// magenta
|
||||||
|
0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF,
|
||||||
|
0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF, 0xFF00FF,
|
||||||
|
|
||||||
|
// yellow
|
||||||
|
0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00,
|
||||||
|
0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00,
|
||||||
|
|
||||||
|
// white
|
||||||
|
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
|
||||||
|
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// initialize SX1278
|
||||||
|
Serial.print(F("[SX1278] Initializing ... "));
|
||||||
|
// carrier frequency: 434.0 MHz
|
||||||
|
// bit rate: 48.0 kbps
|
||||||
|
// frequency deviation: 50.0 kHz
|
||||||
|
// Rx bandwidth: 125.0 kHz
|
||||||
|
// output power: 13 dBm
|
||||||
|
// current limit: 100 mA
|
||||||
|
int state = fsk.beginFSK();
|
||||||
|
if (state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when using one of the non-LoRa modules for SSTV
|
||||||
|
// (RF69, SX1231 etc.), use the basic begin() method
|
||||||
|
// int state = fsk.begin();
|
||||||
|
|
||||||
|
// initialize SSTV client
|
||||||
|
Serial.print(F("[SSTV] Initializing ... "));
|
||||||
|
// SSTV mode: Wrasse (SC2-180)
|
||||||
|
// correction factor: 0.95
|
||||||
|
// NOTE: Due to different speeds of various platforms
|
||||||
|
// supported by RadioLib (Arduino Uno, ESP32 etc),
|
||||||
|
// and because SSTV is analog protocol, incorrect
|
||||||
|
// timing of pulses can lead to distortions.
|
||||||
|
// To compensate, correction factor can be used
|
||||||
|
// to adjust the length of timing pulses
|
||||||
|
// (lower number = shorter pulses).
|
||||||
|
// The value is usually around 0.95 (95%).
|
||||||
|
state = sstv.begin(Wrasse, 0.95);
|
||||||
|
if(state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// to help tune the receiver, SSTVClient can send
|
||||||
|
// continuous 1900 Hz beep
|
||||||
|
/*
|
||||||
|
sstv.idle();
|
||||||
|
while(true);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// send picture with 8 color stripes
|
||||||
|
Serial.print(F("[SSTV] Sending test picture ... "));
|
||||||
|
|
||||||
|
// send synchronization header first
|
||||||
|
sstv.sendHeader();
|
||||||
|
|
||||||
|
// send all picture lines
|
||||||
|
for(uint16_t i = 0; i < sstv.getPictureHeight(); i++) {
|
||||||
|
sstv.sendLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn off transmitter
|
||||||
|
fsk.standby();
|
||||||
|
|
||||||
|
Serial.println(F("done!"));
|
||||||
|
|
||||||
|
delay(30000);
|
||||||
|
}
|
|
@ -155,6 +155,21 @@ const SSTVMode_t PasokonP7 {
|
||||||
|
|
||||||
SSTVClient::SSTVClient(PhysicalLayer* phy) {
|
SSTVClient::SSTVClient(PhysicalLayer* phy) {
|
||||||
_phy = phy;
|
_phy = phy;
|
||||||
|
_audio = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSTVClient::SSTVClient(AFSKClient* audio) {
|
||||||
|
_phy = audio->_phy;
|
||||||
|
_audio = audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t SSTVClient::begin(SSTVMode_t mode, float correction) {
|
||||||
|
if(_audio == nullptr) {
|
||||||
|
// this initialization method can only be used in AFSK mode
|
||||||
|
return(ERR_WRONG_MODEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(begin(0, mode, correction));
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SSTVClient::begin(float base, SSTVMode_t mode, float correction) {
|
int16_t SSTVClient::begin(float base, SSTVMode_t mode, float correction) {
|
||||||
|
@ -170,19 +185,24 @@ int16_t SSTVClient::begin(float base, SSTVMode_t mode, float correction) {
|
||||||
// calculate 24-bit frequency
|
// calculate 24-bit frequency
|
||||||
_base = (base * 1000000.0) / _phy->getFreqStep();
|
_base = (base * 1000000.0) / _phy->getFreqStep();
|
||||||
|
|
||||||
// set module frequency deviation to 0
|
// set module frequency deviation to 0 if using FSK
|
||||||
int16_t state = _phy->setFrequencyDeviation(0);
|
int16_t state = ERR_NONE;
|
||||||
|
if(_audio == nullptr) {
|
||||||
|
state = _phy->setFrequencyDeviation(0);
|
||||||
|
}
|
||||||
|
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSTVClient::idle() {
|
void SSTVClient::idle() {
|
||||||
|
_phy->transmitDirect();
|
||||||
tone(SSTV_TONE_LEADER);
|
tone(SSTV_TONE_LEADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSTVClient::sendHeader() {
|
void SSTVClient::sendHeader() {
|
||||||
// save first header flag for Scottie modes
|
// save first header flag for Scottie modes
|
||||||
_firstLine = true;
|
_firstLine = true;
|
||||||
|
_phy->transmitDirect();
|
||||||
|
|
||||||
// send the first part of header (leader-break-leader)
|
// send the first part of header (leader-break-leader)
|
||||||
tone(SSTV_TONE_LEADER, SSTV_HEADER_LEADER_LENGTH);
|
tone(SSTV_TONE_LEADER, SSTV_HEADER_LEADER_LENGTH);
|
||||||
|
@ -261,7 +281,11 @@ uint16_t SSTVClient::getPictureHeight() {
|
||||||
|
|
||||||
void SSTVClient::tone(float freq, uint32_t len) {
|
void SSTVClient::tone(float freq, uint32_t len) {
|
||||||
uint32_t start = micros();
|
uint32_t start = micros();
|
||||||
_phy->transmitDirect(_base + (freq / _phy->getFreqStep()));
|
if(_audio != nullptr) {
|
||||||
|
_audio->tone(freq, false);
|
||||||
|
} else {
|
||||||
|
_phy->transmitDirect(_base + (freq / _phy->getFreqStep()));
|
||||||
|
}
|
||||||
while(micros() - start < len) {
|
while(micros() - start < len) {
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "../../TypeDef.h"
|
#include "../../TypeDef.h"
|
||||||
#include "../PhysicalLayer/PhysicalLayer.h"
|
#include "../PhysicalLayer/PhysicalLayer.h"
|
||||||
|
#include "../AFSK/AFSK.h"
|
||||||
|
|
||||||
// the following implementation is based on information from
|
// the following implementation is based on information from
|
||||||
// http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
// http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
||||||
|
@ -116,23 +117,45 @@ extern const SSTVMode_t PasokonP7;
|
||||||
class SSTVClient {
|
class SSTVClient {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
\brief Default constructor.
|
\brief Constructor for 2-FSK mode.
|
||||||
|
|
||||||
\param phy Pointer to the wireless module providing PhysicalLayer communication.
|
\param phy Pointer to the wireless module providing PhysicalLayer communication.
|
||||||
*/
|
*/
|
||||||
SSTVClient(PhysicalLayer* phy);
|
SSTVClient(PhysicalLayer* phy);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Constructor for AFSK mode.
|
||||||
|
|
||||||
|
\param audio Pointer to the AFSK instance providing audio.
|
||||||
|
*/
|
||||||
|
SSTVClient(AFSKClient* phy);
|
||||||
|
|
||||||
// basic methods
|
// basic methods
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Initialization method.
|
\brief Initialization method for 2-FSK.
|
||||||
|
|
||||||
\param base Base RF frequency to be used in MHz. In USB modulation, this corresponds to "0 Hz tone".
|
\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.
|
\param mode SSTV mode to be used. Currently supported modes are Scottie1, Scottie2, ScottieDX, Martin1, Martin2, Wrasse, PasokonP3, PasokonP5 and PasokonP7.
|
||||||
|
|
||||||
|
\param correction Timing correction factor, used to adjust the length of timing pulses. Less than 1.0 leads to shorter timing pulses, defaults to 1.0 (no correction).
|
||||||
|
|
||||||
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
int16_t begin(float base, SSTVMode_t mode, float correction = 1.0);
|
int16_t begin(float base, SSTVMode_t mode, float correction = 1.0);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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.
|
||||||
|
|
||||||
|
\param correction Timing correction factor, used to adjust the length of timing pulses. Less than 1.0 leads to shorter timing pulses, defaults to 1.0 (no correction).
|
||||||
|
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t begin(SSTVMode_t mode, float correction = 1.0);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sends out tone at 1900 Hz.
|
\brief Sends out tone at 1900 Hz.
|
||||||
*/
|
*/
|
||||||
|
@ -161,6 +184,7 @@ class SSTVClient {
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
PhysicalLayer* _phy;
|
PhysicalLayer* _phy;
|
||||||
|
AFSKClient* _audio;
|
||||||
|
|
||||||
uint32_t _base;
|
uint32_t _base;
|
||||||
SSTVMode_t _mode;
|
SSTVMode_t _mode;
|
||||||
|
|
Loading…
Add table
Reference in a new issue