[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) {
|
||||
_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) {
|
||||
|
@ -170,19 +185,24 @@ int16_t SSTVClient::begin(float base, SSTVMode_t mode, float correction) {
|
|||
// calculate 24-bit frequency
|
||||
_base = (base * 1000000.0) / _phy->getFreqStep();
|
||||
|
||||
// set module frequency deviation to 0
|
||||
int16_t state = _phy->setFrequencyDeviation(0);
|
||||
// set module frequency deviation to 0 if using FSK
|
||||
int16_t state = ERR_NONE;
|
||||
if(_audio == nullptr) {
|
||||
state = _phy->setFrequencyDeviation(0);
|
||||
}
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
void SSTVClient::idle() {
|
||||
_phy->transmitDirect();
|
||||
tone(SSTV_TONE_LEADER);
|
||||
}
|
||||
|
||||
void SSTVClient::sendHeader() {
|
||||
// save first header flag for Scottie modes
|
||||
_firstLine = true;
|
||||
_phy->transmitDirect();
|
||||
|
||||
// send the first part of header (leader-break-leader)
|
||||
tone(SSTV_TONE_LEADER, SSTV_HEADER_LEADER_LENGTH);
|
||||
|
@ -261,7 +281,11 @@ uint16_t SSTVClient::getPictureHeight() {
|
|||
|
||||
void SSTVClient::tone(float freq, uint32_t len) {
|
||||
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) {
|
||||
yield();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "../../TypeDef.h"
|
||||
#include "../PhysicalLayer/PhysicalLayer.h"
|
||||
#include "../AFSK/AFSK.h"
|
||||
|
||||
// the following implementation is based on information from
|
||||
// http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
||||
|
@ -116,23 +117,45 @@ extern const SSTVMode_t PasokonP7;
|
|||
class SSTVClient {
|
||||
public:
|
||||
/*!
|
||||
\brief Default constructor.
|
||||
\brief Constructor for 2-FSK mode.
|
||||
|
||||
\param phy Pointer to the wireless module providing PhysicalLayer communication.
|
||||
*/
|
||||
SSTVClient(PhysicalLayer* phy);
|
||||
|
||||
/*!
|
||||
\brief Constructor for AFSK mode.
|
||||
|
||||
\param audio Pointer to the AFSK instance providing audio.
|
||||
*/
|
||||
SSTVClient(AFSKClient* phy);
|
||||
|
||||
// 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 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);
|
||||
|
||||
/*!
|
||||
\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.
|
||||
*/
|
||||
|
@ -161,6 +184,7 @@ class SSTVClient {
|
|||
private:
|
||||
#endif
|
||||
PhysicalLayer* _phy;
|
||||
AFSKClient* _audio;
|
||||
|
||||
uint32_t _base;
|
||||
SSTVMode_t _mode;
|
||||
|
|
Loading…
Add table
Reference in a new issue