BaliseHF/src/wsprPico_GPS_RP2350/lib/Modulation/Modulation.cpp

348 lines
13 KiB
C++

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: Modulation.cpp
* Author: ale
*
* Created on 28 août 2025, 07:55
*/
#include "Modulation.h"
Modulation::Modulation(config* _cfg) : cfg(_cfg) {
setFreqWspr(cfg->freq+cfg->offset);
setFreqRtty(cfg->freq+cfg->offset+500L); //a cote de la fréquence wspr
setFreqFt8(cfg->freq+cfg->offset+500L);
setFreqCW(cfg->freq+cfg->offset+500L);
anchor = this;
pinMode(TEST_PIN,OUTPUT);
}
Modulation::Modulation(const Modulation& orig) {
}
Modulation::~Modulation() {
}
void Modulation::marshall() {
// Clear the alarm irq
hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM);
// Reset the alarm register
timer_hw->alarm[ALARM_NUM] = timer_hw->timerawl + period_us ;
anchor->timerCallback();
}
void Modulation::startTimerIRQ(uint32_t us) {
period_us = us;
// Enable the interrupt for the alarm (we're using Alarm 0)
hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM) ;
// Associate an interrupt handler with the ALARM_IRQ
irq_set_exclusive_handler(ALARM_IRQ, marshall) ;
// Enable the alarm interrupt
irq_set_enabled(ALARM_IRQ, true) ;
// Write the lower 32 bits of the target time to the alarm register, arming it.
timer_hw->alarm[ALARM_NUM] = timer_hw->timerawl + period_us ;
irq_set_priority(ALARM_IRQ, 0); // Priorité haute
Serial.println("init irq");
}
void Modulation::stopTimerIRQ() {
irq_set_enabled(ALARM_IRQ, false);
hardware_alarm_unclaim(ALARM_NUM);
Serial.println("stop irq");
}
void Modulation::timerCallback() {
gpio_xor_mask(1u << TEST_PIN); // Toggle rapide
flagIrq = true;
//Serial.print("irq");
}
void Modulation::waitIrq(){
while (!flagIrq){}
flagIrq=false;
}
void Modulation::sendWspr(char *locator) {
memset(txBuffer, 0, WSPR_SYMBOL_COUNT);
if (cfg->follow) {
jtencode.wspr_encode(cfg->call, locator, dbm, txBuffer); //avec suivi
} else {
jtencode.wspr_encode(cfg->call, locator, cfg->dbm, txBuffer); //sans suivi
}
startTimerIRQ(WSPR_DELAY * 1000);
flagIrq = false;
for (uint8_t i = 0; i < WSPR_SYMBOL_COUNT; i++) {
periods = wsfr[txBuffer[i]]; //modulation fsk sur 4 niveaux de fréquences avec un shift de 1.48 Hz le contenu de buffer est 0 ou 1 ou 2 ou 3
//delay(WSPR_DELAY);
waitIrq();
}
periods = 200 << 24; //dds off
stopTimerIRQ();
}
void Modulation::setDbm(uint8_t _dbm){
dbm=_dbm;
}
void Modulation::sendRtty(char* stringRtty) {
const static int TableRtty[59] PROGMEM = { 4, 22, 17, 5, 18, 0, 11, 26, 30, 9, 0, 0, 6, 24, 7, 23, 13, 29, 25, 16, 10, 1, 21, 28, 12, 3, 14, 15, 0, 0, 0, 19, 0, 24, 19, 14, 18, 16, 22, 11, 5, 12, 26, 30, 9, 7, 6, 3, 13, 29, 10, 20, 1, 28, 15, 25, 23, 21, 17 };
//tableau dans l'ordre ascii qui démarre à partir de l'espace 32
//les valeurs représentent le code baudot
int signlett = 1; // RTTY Baudot signs/letters tables toggle
char c;
startTimerIRQ(RTTY_DELAY*1000);
c = *stringRtty++;
for (int n = 1; n < 20; n++) { //sync on envoie une série de letters table code
rttyTxByte(27);
}
while (c != '\0') {
//Serial.print(c);
c = toupper(int(c)); // Uppercase
if (c == 10) // Line Feed
{
rttyTxByte(8);
} else if (c == 13) // Carriage Return
{
rttyTxByte(2);
} else if (c == 32) // Space
{
rttyTxByte(4);
} else if (c > 32 && c < 91) {
c = c - 32;
if (c < 33) {
if (signlett == 1) {
signlett = 0; // toggle form signs to letters table
rttyTxByte(27); //
}
} else if (signlett == 0) {
signlett = 1; // toggle form letters to signs table
rttyTxByte(31); //
}
rttyTxByte(int(pgm_read_word(&TableRtty[int(c)]))); // Send the 5 bits word
}
c = *stringRtty++; // Next character in string
}
periods = 200 << 24; //dds off
stopTimerIRQ();
}
void Modulation::rttyTxByte(char c) {
int val;
c = (c << 2) + 3; //ajoute les deux bits de stop
for (int b = 7; b >= 0; b--) // MSB first
{
val = bitRead(c, b); // Read 1 bit
if (val == 0) periods = rttyfr[0]; //modulation fsk
else periods = rttyfr[1];
//delay(RTTY_DELAY);
waitIrq();
}
}
void Modulation::sendFt8(char *call, char *locator) {
char buf[20];
sprintf(buf, "%s%s", call, locator);
//Serial.println(buf);
memset(txBuffer, 0, FT8_SYMBOL_COUNT);
jtencode.ft8_encode(buf, txBuffer);
//debug
/*
int n, lf;
lf = 0;
for (n = 0; n < FT8_SYMBOL_COUNT; n++) { //print symbols on serial monitor
if (lf % 16 == 0) {
Serial.println();
Serial.print(n);
Serial.print(": ");
}
lf++;
Serial.print(txBuffer[n]);
Serial.print(',');
}
Serial.println();
*/
startTimerIRQ(FT8_DELAY*1000);
flagIrq=false;
for (uint8_t i = 0; i < FT8_SYMBOL_COUNT; i++) {
periods = ft8fr[txBuffer[i]];
//delay(FT8_DELAY);
waitIrq();
}
periods = 200 << 24; //dds off
stopTimerIRQ();
}
/***********************************************************************************
* Hellschreiber 122.5 bauds => 8,163ms
* http://brainwagon.org/2012/01/11/hellduino-sending-hellschreiber-from-an-arduino
************************************************************************************/
void Modulation::sendHell(char * stringHell) {
static word GlyphTab[59][8] PROGMEM = {
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x1f9c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0330, 0x0ffc, 0x0330, 0x0ffc, 0x0330, 0x0000, 0x0000},
{0x078c, 0x0ccc, 0x1ffe, 0x0ccc, 0x0c78, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x01e0, 0x0738, 0x1c0e, 0x0000, 0x0000, 0x0000, 0x0000},
{0x1c0e, 0x0738, 0x01e0, 0x0000, 0x0000, 0x0000, 0x0000},
{0x018c, 0x0198, 0x0ff0, 0x0198, 0x018c, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x001c, 0x001c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x001c, 0x0070, 0x01c0, 0x0700, 0x1c00, 0x0000, 0x0000},
{0x07f8, 0x0c0c, 0x0c0c, 0x0c0c, 0x07f8, 0x0000, 0x0000},
{0x0300, 0x0600, 0x0ffc, 0x0000, 0x0000, 0x0000, 0x0000},
{0x061c, 0x0c3c, 0x0ccc, 0x078c, 0x000c, 0x0000, 0x0000},
{0x0006, 0x1806, 0x198c, 0x1f98, 0x00f0, 0x0000, 0x0000},
{0x1fe0, 0x0060, 0x0060, 0x0ffc, 0x0060, 0x0000, 0x0000},
{0x000c, 0x000c, 0x1f8c, 0x1998, 0x18f0, 0x0000, 0x0000},
{0x07fc, 0x0c66, 0x18c6, 0x00c6, 0x007c, 0x0000, 0x0000},
{0x181c, 0x1870, 0x19c0, 0x1f00, 0x1c00, 0x0000, 0x0000},
{0x0f3c, 0x19e6, 0x18c6, 0x19e6, 0x0f3c, 0x0000, 0x0000},
{0x0f80, 0x18c6, 0x18cc, 0x18cc, 0x0ff0, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x1800, 0x1800, 0x19ce, 0x1f00, 0x0000, 0x0000, 0x0000},
{0x01f0, 0x0208, 0x04e4, 0x0514, 0x0514, 0x03e0, 0x0000},
{0x07fc, 0x0e60, 0x0c60, 0x0e60, 0x07fc, 0x0000, 0x0000},
{0x0c0c, 0x0ffc, 0x0ccc, 0x0ccc, 0x0738, 0x0000, 0x0000},
{0x0ffc, 0x0c0c, 0x0c0c, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
{0x0c0c, 0x0ffc, 0x0c0c, 0x0c0c, 0x07f8, 0x0000, 0x0000},
{0x0ffc, 0x0ccc, 0x0ccc, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
{0x0ffc, 0x0cc0, 0x0cc0, 0x0c00, 0x0c00, 0x0000, 0x0000},
{0x0ffc, 0x0c0c, 0x0c0c, 0x0ccc, 0x0cfc, 0x0000, 0x0000},
{0x0ffc, 0x00c0, 0x00c0, 0x00c0, 0x0ffc, 0x0000, 0x0000},
{0x0c0c, 0x0c0c, 0x0ffc, 0x0c0c, 0x0c0c, 0x0000, 0x0000},
{0x003c, 0x000c, 0x000c, 0x000c, 0x0ffc, 0x0000, 0x0000},
{0x0ffc, 0x00c0, 0x00e0, 0x0330, 0x0e1c, 0x0000, 0x0000},
{0x0ffc, 0x000c, 0x000c, 0x000c, 0x000c, 0x0000, 0x0000},
{0x0ffc, 0x0600, 0x0300, 0x0600, 0x0ffc, 0x0000, 0x0000},
{0x0ffc, 0x0700, 0x01c0, 0x0070, 0x0ffc, 0x0000, 0x0000},
{0x0ffc, 0x0c0c, 0x0c0c, 0x0c0c, 0x0ffc, 0x0000, 0x0000},
{0x0c0c, 0x0ffc, 0x0ccc, 0x0cc0, 0x0780, 0x0000, 0x0000},
{0x0ffc, 0x0c0c, 0x0c3c, 0x0ffc, 0x000f, 0x0000, 0x0000},
{0x0ffc, 0x0cc0, 0x0cc0, 0x0cf0, 0x079c, 0x0000, 0x0000},
{0x078c, 0x0ccc, 0x0ccc, 0x0ccc, 0x0c78, 0x0000, 0x0000},
{0x0c00, 0x0c00, 0x0ffc, 0x0c00, 0x0c00, 0x0000, 0x0000},
{0x0ff8, 0x000c, 0x000c, 0x000c, 0x0ff8, 0x0000, 0x0000},
{0x0ffc, 0x0038, 0x00e0, 0x0380, 0x0e00, 0x0000, 0x0000},
{0x0ff8, 0x000c, 0x00f8, 0x000c, 0x0ff8, 0x0000, 0x0000},
{0x0e1c, 0x0330, 0x01e0, 0x0330, 0x0e1c, 0x0000, 0x0000},
{0x0e00, 0x0380, 0x00fc, 0x0380, 0x0e00, 0x0000, 0x0000},
{0x0c1c, 0x0c7c, 0x0ccc, 0x0f8c, 0x0e0c, 0x0000, 0x0000}
};
int val;
char ch;
word fbits;
startTimerIRQ(HELL_DELAY);
ch = *stringHell++;
while (ch != '\0') {
ch = toupper(int(ch)); // Uppercase
if (ch >= 32 && ch <= 90) // Character is in the range of ASCII space to Z
{
ch -= 32; // Character number starting at 0
for (int i = 0; i < 7; i++) // Scanning each 7 columns of glyph
{
fbits = int(pgm_read_word(&GlyphTab[int(ch)][i])); // Get each column of glyph
for (int b = 0; b < 14; b++) // Scanning each 14 rows
{
val = bitRead(fbits, b); // Get binary state of pixel
//DDS.setfreq(freqHell * val, 0); // Let's transmit (or not if pixel is clear)
if (val){
periods = rttyfr[0]; //utiliser la fréquence du rtty pour essais
}
else{
periods = 200 << 24; //dds off
}
waitIrq();
//delayMicroseconds(HELL_DELAY); //8,163ms/2
}
}
}
ch = *stringHell++; // Next character in string
}
periods = 200 << 24; //dds off
stopTimerIRQ();
}
/***************************************************************************
* CW
***************************************************************************/
void Modulation::sendCw(char * stringCw, int cwWpm) {
static int morseVaricode[2][59] PROGMEM = {
{0, 212, 72, 0, 144, 0, 128, 120, 176, 180, 0, 80, 204, 132, 84, 144, 248, 120, 56, 24, 8, 0, 128, 192, 224, 240, 224, 168, 0, 136, 0, 48, 104, 64, 128, 160, 128, 0, 32, 192, 0, 0, 112, 160, 64, 192, 128, 224, 96, 208, 64, 0, 128, 32, 16, 96, 144, 176, 192},
{7, 6, 5, 0, 4, 0, 4, 6, 5, 6, 0, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 0, 5, 0, 6, 6, 2, 4, 4, 3, 1, 4, 3, 4, 2, 4, 3, 4, 2, 2, 3, 4, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4}
};
int tempo = 1200 / cwWpm; // Duration of 1 dot
byte nb_bits, val;
int d;
int c = *stringCw++;
while (c != '\0') {
c = toupper(c); // Uppercase
if (c == 32) { // Space character between words in string
periods = 200 << 24; //dds off 7 dot length spacing
delay(tempo * 7); // between words
} else if (c > 32 && c < 91) {
c = c - 32;
d = int(pgm_read_word(&morseVaricode[0][c])); // Get CW varicode
nb_bits = int(pgm_read_word(&morseVaricode[1][c])); // Get CW varicode length
if (nb_bits != 0) { // Number of bits = 0 -> invalid character #%<>
for (int b = 7; b > 7 - nb_bits; b--) { // Send CW character, each bit represents a symbol (0 for dot, 1 for dash) MSB first
val = bitRead(d, b); //look varicode
periods = cwfr;
delay(tempo + 2 * tempo * val); // A dot length or a dash length (3 times the dot)
periods = 200 << 24; //dds off 1 dot length spacing
delay(tempo); // between symbols in a character
}
}
periods = 200 << 24; //dds off 3 dots length spacing
delay(tempo * 3); // between characters in a word
}
c = *stringCw++; // Next caracter in string
}
periods = 200 << 24; //dds off
}
void Modulation::sendCarriage(bool state) {
if (state) {
periods = cwfr;
} else {
periods = 200 << 24;
}
}
Modulation* Modulation::anchor = NULL;
uint32_t Modulation::period_us;