kopia lustrzana https://github.com/jamescoxon/dl-fldigi
1000 wiersze
32 KiB
C++
1000 wiersze
32 KiB
C++
// ----------------------------------------------------------------------------
|
|
// modem.cxx - modem class - base for all modems
|
|
//
|
|
// Copyright (C) 2006-2010
|
|
// Dave Freese, W1HKJ
|
|
//
|
|
// This file is part of fldigi.
|
|
//
|
|
// Fldigi is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Fldigi is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include <config.h>
|
|
|
|
#include <string>
|
|
|
|
#include "misc.h"
|
|
#include "filters.h"
|
|
|
|
#include "confdialog.h"
|
|
#include "modem.h"
|
|
#include "trx.h"
|
|
#include "fl_digi.h"
|
|
#include "main.h"
|
|
#include "arq_io.h"
|
|
#include "configuration.h"
|
|
#include "waterfall.h"
|
|
#include "qrunner.h"
|
|
|
|
#include "status.h"
|
|
#include "debug.h"
|
|
|
|
using namespace std;
|
|
|
|
modem *null_modem = 0;
|
|
modem *cw_modem = 0;
|
|
|
|
modem *mfsk8_modem = 0;
|
|
modem *mfsk16_modem = 0;
|
|
modem *mfsk32_modem = 0;
|
|
// experimental modes
|
|
modem *mfsk4_modem = 0;
|
|
modem *mfsk11_modem = 0;
|
|
modem *mfsk22_modem = 0;
|
|
modem *mfsk31_modem = 0;
|
|
modem *mfsk64_modem = 0;
|
|
|
|
modem *wefax576 = 0;
|
|
modem *wefax288 = 0;
|
|
|
|
modem *mt63_500_modem = 0;
|
|
modem *mt63_1000_modem = 0;
|
|
modem *mt63_2000_modem = 0;
|
|
|
|
modem *feld_modem = 0;
|
|
modem *feld_slowmodem = 0;
|
|
modem *feld_x5modem = 0;
|
|
modem *feld_x9modem = 0;
|
|
modem *feld_FMmodem = 0;
|
|
modem *feld_FM105modem = 0;
|
|
modem *feld_80modem = 0;
|
|
modem *feld_CMTmodem = 0;
|
|
|
|
modem *psk31_modem = 0;
|
|
modem *psk63_modem = 0;
|
|
modem *psk63f_modem = 0;
|
|
modem *psk125_modem = 0;
|
|
modem *psk250_modem = 0;
|
|
modem *psk500_modem = 0;
|
|
|
|
modem *qpsk31_modem = 0;
|
|
modem *qpsk63_modem = 0;
|
|
modem *qpsk125_modem = 0;
|
|
modem *qpsk250_modem = 0;
|
|
modem *qpsk500_modem = 0;
|
|
|
|
modem *psk125r_modem = 0;
|
|
modem *psk250r_modem = 0;
|
|
modem *psk500r_modem = 0;
|
|
|
|
modem *olivia_modem = 0;
|
|
modem *contestia_modem = 0;
|
|
|
|
modem *rtty_modem = 0;
|
|
|
|
modem *thor4_modem = 0;
|
|
modem *thor5_modem = 0;
|
|
modem *thor8_modem = 0;
|
|
modem *thor11_modem = 0;
|
|
//modem *tsor11_modem = 0;
|
|
modem *thor16_modem = 0;
|
|
modem *thor22_modem = 0;
|
|
|
|
modem *dominoex4_modem = 0;
|
|
modem *dominoex5_modem = 0;
|
|
modem *dominoex8_modem = 0;
|
|
modem *dominoex11_modem = 0;
|
|
modem *dominoex16_modem = 0;
|
|
modem *dominoex22_modem = 0;
|
|
|
|
modem *throb1_modem = 0;
|
|
modem *throb2_modem = 0;
|
|
modem *throb4_modem = 0;
|
|
modem *throbx1_modem = 0;
|
|
modem *throbx2_modem = 0;
|
|
modem *throbx4_modem = 0;
|
|
|
|
modem *wwv_modem = 0;
|
|
modem *anal_modem = 0;
|
|
modem *ssb_modem = 0;
|
|
|
|
trx_mode modem::get_mode()
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
modem::modem()
|
|
{
|
|
scptr = 0;
|
|
freqlock = false;
|
|
sigsearch = 0;
|
|
bool wfrev = wf->Reverse();
|
|
bool wfsb = wf->USB();
|
|
reverse = wfrev ^ !wfsb;
|
|
historyON = false;
|
|
cap = CAP_RX | CAP_TX;
|
|
PTTphaseacc = 0.0;
|
|
frequency = 1000.0;
|
|
s2n_ncount = s2n_sum = s2n_sum2 = s2n_metric = 0.0;
|
|
s2n_valid = false;
|
|
track_freq_lock = 0;
|
|
}
|
|
|
|
// modem types CW and RTTY do not use the base init()
|
|
void modem::init()
|
|
{
|
|
bool wfrev = wf->Reverse();
|
|
bool wfsb = wf->USB();
|
|
reverse = wfrev ^ !wfsb;
|
|
|
|
if (progdefaults.StartAtSweetSpot) {
|
|
set_freq(progdefaults.PSKsweetspot);
|
|
} else if (progStatus.carrier != 0) {
|
|
set_freq(progStatus.carrier);
|
|
#if !BENCHMARK_MODE
|
|
progStatus.carrier = 0;
|
|
#endif
|
|
} else
|
|
set_freq(wf->Carrier());
|
|
stopflag = false;
|
|
}
|
|
|
|
double modem::track_freq(double freq)
|
|
{
|
|
if(track_freq_lock) return(freq);
|
|
|
|
if(freq >= progdefaults.track_freq_min &&
|
|
freq <= progdefaults.track_freq_max)
|
|
return(freq);
|
|
|
|
double rf = wf->rfcarrier();
|
|
double cf = (progdefaults.track_freq_max + progdefaults.track_freq_min) / 2;
|
|
|
|
if(rf <= 0) return(freq);
|
|
|
|
if(wf->USB()) rf += freq - cf;
|
|
else rf -= freq - cf;
|
|
|
|
qsy(rf);
|
|
|
|
return(cf);
|
|
}
|
|
|
|
void modem::set_freq(double freq)
|
|
{
|
|
if(progdefaults.track_freq)
|
|
freq = track_freq(freq);
|
|
|
|
frequency = CLAMP(
|
|
freq,
|
|
progdefaults.LowFreqCutoff + bandwidth / 2,
|
|
progdefaults.HighFreqCutoff - bandwidth / 2);
|
|
if (freqlock == false)
|
|
tx_frequency = frequency;
|
|
REQ(put_freq, frequency);
|
|
}
|
|
|
|
void modem::set_freqlock(bool on)
|
|
{
|
|
freqlock = on;
|
|
set_freq(frequency);
|
|
}
|
|
|
|
|
|
bool modem::freqlocked()
|
|
{
|
|
return freqlock;
|
|
}
|
|
|
|
double modem::get_txfreq(void)
|
|
{
|
|
if (unlikely(!(cap & CAP_TX)))
|
|
return 0;
|
|
else if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
return progdefaults.PSKsweetspot;
|
|
return tx_frequency;
|
|
}
|
|
|
|
double modem::get_txfreq_woffset(void)
|
|
{
|
|
if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
return (progdefaults.PSKsweetspot - progdefaults.TxOffset);
|
|
return (tx_frequency - progdefaults.TxOffset);
|
|
}
|
|
|
|
int modem::get_freq()
|
|
{
|
|
return (int)(frequency + 0.5);
|
|
}
|
|
|
|
double modem::get_bandwidth(void)
|
|
{
|
|
return bandwidth;
|
|
}
|
|
|
|
void modem::set_bandwidth(double bw)
|
|
{
|
|
bandwidth = bw;
|
|
put_Bandwidth((int)bandwidth);
|
|
}
|
|
|
|
void modem::set_reverse(bool on)
|
|
{
|
|
reverse = on ^ (!wf->USB());
|
|
}
|
|
|
|
void modem::set_metric(double m)
|
|
{
|
|
metric = m;
|
|
}
|
|
|
|
void modem::display_metric(double m)
|
|
{
|
|
set_metric(m);
|
|
::global_display_metric(m);
|
|
}
|
|
|
|
double modem::get_metric(void)
|
|
{
|
|
return metric;
|
|
}
|
|
|
|
|
|
bool modem::get_cwTrack()
|
|
{
|
|
return cwTrack;
|
|
}
|
|
|
|
void modem::set_cwTrack(bool b)
|
|
{
|
|
cwTrack = b;
|
|
}
|
|
|
|
bool modem::get_cwLock()
|
|
{
|
|
return cwLock;
|
|
}
|
|
|
|
void modem::set_cwLock(bool b)
|
|
{
|
|
cwLock = b;
|
|
}
|
|
|
|
double modem::get_cwRcvWPM()
|
|
{
|
|
return cwRcvWPM;
|
|
}
|
|
|
|
double modem::get_cwXmtWPM()
|
|
{
|
|
return cwXmtWPM;
|
|
}
|
|
|
|
void modem::set_cwXmtWPM(double wpm)
|
|
{
|
|
cwXmtWPM = wpm;
|
|
}
|
|
|
|
int modem::get_samplerate(void)
|
|
{
|
|
return samplerate;
|
|
}
|
|
|
|
void modem::set_samplerate(int smprate)
|
|
{
|
|
samplerate = smprate;
|
|
}
|
|
|
|
double modem::PTTnco()
|
|
{
|
|
PTTphaseacc += TWOPI * 1000 / samplerate;
|
|
if (PTTphaseacc > M_PI)
|
|
PTTphaseacc -= TWOPI;
|
|
return sin(PTTphaseacc);
|
|
}
|
|
|
|
double modem::sigmaN (double es_ovr_n0)
|
|
{
|
|
double sn_ratio, sigma;
|
|
double mode_factor = 0.707;
|
|
switch (mode) {
|
|
case MODE_CW:
|
|
mode_factor /= 0.44;
|
|
break;
|
|
case MODE_FELDHELL: case MODE_SLOWHELL:
|
|
case MODE_HELLX5: case MODE_HELLX9:
|
|
mode_factor /= 0.22;
|
|
break;
|
|
case MODE_MT63_500: case MODE_MT63_1000: case MODE_MT63_2000 :
|
|
mode_factor *= 3.0;
|
|
break;
|
|
case MODE_PSK31: case MODE_PSK63: case MODE_PSK63F:
|
|
case MODE_PSK125: case MODE_PSK250: case MODE_PSK500:
|
|
case MODE_QPSK31: case MODE_QPSK63: case MODE_QPSK125: case MODE_QPSK250:
|
|
case MODE_PSK125R: case MODE_PSK250R: case MODE_PSK500R:
|
|
mode_factor *= 16;
|
|
break;
|
|
case MODE_THROB1: case MODE_THROB2: case MODE_THROB4:
|
|
case MODE_THROBX1: case MODE_THROBX2: case MODE_THROBX4:
|
|
mode_factor *= 6.0;
|
|
break;
|
|
// case MODE_RTTY:
|
|
// case MODE_OLIVIA:
|
|
// case MODE_DOMINOEX4: case MODE_DOMINOEX5: case MODE_DOMINOEX8:
|
|
// case MODE_DOMINOEX11: case MODE_DOMINOEX16: case MODE_DOMINOEX22:
|
|
// case MODE_MFSK4: case MODE_MFSK11: case MODE_MFSK22: case MODE_MFSK31:
|
|
// case MODE_MFSK64: case MODE_MFSK8: case MODE_MFSK16: case MODE_MFSK32:
|
|
// case MODE_THOR4: case MODE_THOR5: case MODE_THOR8:
|
|
// case MODE_THOR11:case MODE_THOR16: case MODE_THOR22:
|
|
// case MODE_FSKHELL: case MODE_FSKH105: case MODE_HELL80:
|
|
default: break;
|
|
}
|
|
if (trx_state == STATE_TUNE) mode_factor = 0.707;
|
|
|
|
sn_ratio = pow(10, ( es_ovr_n0 / 10) );
|
|
sigma = sqrt ( mode_factor / sn_ratio );
|
|
return sigma;
|
|
}
|
|
|
|
// A Rayleigh-distributed random variable R, with the probability
|
|
// distribution
|
|
// F(R) = 0 where R < 0 and
|
|
// F(R) = 1 - exp(-R^2/2*sigma^2) where R >= 0,
|
|
// is related to a pair of Gaussian variables C and D
|
|
// through the transformation
|
|
// C = R * cos(theta) and
|
|
// D = R * sin(theta),
|
|
// where theta is a uniformly distributed variable in the interval
|
|
// 0 to 2 * Pi.
|
|
|
|
double modem::gauss(double sigma) {
|
|
double u, r;
|
|
u = 1.0 * rand() / RAND_MAX;
|
|
r = sigma * sqrt( 2.0 * log( 1.0 / (1.0 - u) ) );
|
|
u = 1.0 * rand() / RAND_MAX;
|
|
return r * cos(2 * M_PI * u);
|
|
}
|
|
|
|
// given the desired Es/No, calculate the standard deviation of the
|
|
// additive white gaussian noise (AWGN). The standard deviation of
|
|
// the AWGN will be used to generate Gaussian random variables
|
|
// simulating the noise that is added to the signal.
|
|
// return signal + noise, limiting value to +/- 1.0
|
|
|
|
void modem::add_noise(double *buffer, int len)
|
|
{
|
|
double sigma = sigmaN(progdefaults.s2n);
|
|
double sn = 0;
|
|
for (int n = 0; n < len; n++) {
|
|
sn = (buffer[n] + gauss(sigma)) / (1.0 + 3.0 * sigma);
|
|
buffer[n] = clamp(sn, -1.0, 1.0);
|
|
}
|
|
}
|
|
|
|
void modem::s2nreport(void)
|
|
{
|
|
double s2n_avg = s2n_sum / s2n_ncount;
|
|
double s2n_stddev = sqrt((s2n_sum2 / s2n_ncount) - (s2n_avg * s2n_avg));
|
|
|
|
REQ(pskmail_notify_s2n, s2n_ncount, s2n_avg, s2n_stddev);
|
|
}
|
|
|
|
void modem::ModulateXmtr(double *buffer, int len)
|
|
{
|
|
if (progdefaults.PTTrightchannel) {
|
|
for (int i = 0; i < len; i++)
|
|
PTTchannel[i] = PTTnco();
|
|
ModulateStereo( buffer, PTTchannel, len);
|
|
return;
|
|
}
|
|
|
|
if (progdefaults.viewXmtSignal)
|
|
trx_xmit_wfall_queue(samplerate, buffer, (size_t)len);
|
|
|
|
if (withnoise && progdefaults.noise) add_noise(buffer, len);
|
|
|
|
double mult = pow(10, progdefaults.txlevel / 20.0);
|
|
for (int i = 0; i < len; i++) buffer[i] *= mult;
|
|
|
|
try {
|
|
unsigned n = 4;
|
|
while (scard->Write(buffer, len) == 0 && --n);
|
|
if (n == 0)
|
|
throw SndException("Sound write failed");
|
|
}
|
|
catch (const SndException& e) {
|
|
LOG_ERROR("%s", e.what());
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
#include <iostream>
|
|
using namespace std;
|
|
void modem::ModulateStereo(double *left, double *right, int len)
|
|
{
|
|
if (progdefaults.viewXmtSignal)
|
|
trx_xmit_wfall_queue(samplerate, left, (size_t)len);
|
|
|
|
if (withnoise && progdefaults.noise) add_noise(left, len);
|
|
|
|
double mult = pow(10, progdefaults.txlevel / 20.0);
|
|
for (int i = 0; i < len; i++) left[i] *= mult;
|
|
|
|
try {
|
|
unsigned n = 4;
|
|
while (scard->Write_stereo(left, right, len) == 0 && --n);
|
|
if (n == 0)
|
|
throw SndException("Sound write failed");
|
|
}
|
|
catch (const SndException& e) {
|
|
LOG_ERROR("%s", e.what());
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void modem::videoText()
|
|
{
|
|
if (trx_state == STATE_TUNE)
|
|
return;
|
|
|
|
if (progdefaults.pretone > 0.2)
|
|
pretone();
|
|
|
|
if (progdefaults.sendtextid == true) {
|
|
wfid_text(progdefaults.strTextid);
|
|
} else if (progdefaults.macrotextid == true) {
|
|
wfid_text(progdefaults.strTextid);
|
|
progdefaults.macrotextid = false;
|
|
}
|
|
|
|
if (progdefaults.videoid_modes.test(mode) &&
|
|
(progdefaults.sendid || progdefaults.macroid)) {
|
|
#define TLEN 20
|
|
char idtxt[TLEN] = "";
|
|
switch(mode_info[mode].mode) {
|
|
case MODE_CONTESTIA:
|
|
snprintf(idtxt, TLEN, "%s-%d/%d", mode_info[mode].vid_name,
|
|
2*(1<<progdefaults.contestiatones),
|
|
125*(1<<progdefaults.contestiabw));
|
|
break;
|
|
case MODE_OLIVIA:
|
|
snprintf(idtxt, TLEN, "%s-%d/%d", mode_info[mode].vid_name,
|
|
2*(1<<progdefaults.oliviatones),
|
|
125*(1<<progdefaults.oliviabw));
|
|
break;
|
|
case MODE_RTTY:
|
|
snprintf(idtxt, TLEN, "%s-%d/%d", mode_info[mode].vid_name,
|
|
static_cast<int>(rtty::BAUD[progdefaults.rtty_baud]),
|
|
rtty::BITS[progdefaults.rtty_bits]);
|
|
break;
|
|
case MODE_DOMINOEX4: case MODE_DOMINOEX5: case MODE_DOMINOEX8:
|
|
case MODE_DOMINOEX11: case MODE_DOMINOEX16: case MODE_DOMINOEX22:
|
|
if (progdefaults.DOMINOEX_FEC)
|
|
snprintf(idtxt, TLEN, "%s-FEC", mode_info[mode].vid_name);
|
|
else
|
|
strcpy(idtxt, mode_info[mode].vid_name);
|
|
break;
|
|
default:
|
|
strcpy(idtxt, mode_info[mode].vid_name);
|
|
break;
|
|
}
|
|
wfid_text(idtxt);
|
|
progdefaults.macroid = false;
|
|
}
|
|
}
|
|
|
|
// CW ID transmit routines
|
|
|
|
//===========================================================================
|
|
// cw transmit routines to send a post amble message
|
|
// Define the amplitude envelop for key down events
|
|
// this is 1/2 cycle of a raised cosine
|
|
//===========================================================================
|
|
|
|
void modem::cwid_makeshape()
|
|
{
|
|
for (int i = 0; i < 128; i++) cwid_keyshape[i] = 1.0;
|
|
for (int i = 0; i < RT; i++)
|
|
cwid_keyshape[i] = 0.5 * (1.0 - cos (M_PI * i / RT));
|
|
}
|
|
|
|
double modem::cwid_nco(double freq)
|
|
{
|
|
cwid_phaseacc += 2.0 * M_PI * freq / samplerate;
|
|
|
|
if (cwid_phaseacc > M_PI)
|
|
cwid_phaseacc -= 2.0 * M_PI;
|
|
|
|
return sin(cwid_phaseacc);
|
|
}
|
|
|
|
//=====================================================================
|
|
// cwid_send_symbol()
|
|
// Sends a part of a morse character (one dot duration) of either
|
|
// sound at the correct freq or silence. Rise and fall time is controlled
|
|
// with a raised cosine shape.
|
|
//
|
|
// Left channel contains the shaped A2 CW waveform
|
|
//=======================================================================
|
|
|
|
void modem::cwid_send_symbol(int bits)
|
|
{
|
|
double freq;
|
|
int i,
|
|
keydown,
|
|
keyup,
|
|
sample = 0,
|
|
currsym = bits & 1;
|
|
|
|
freq = tx_frequency - progdefaults.TxOffset;
|
|
|
|
if ((currsym == 1) && (cwid_lastsym == 0))
|
|
cwid_phaseacc = 0.0;
|
|
|
|
keydown = cwid_symbollen - RT;
|
|
keyup = cwid_symbollen - RT;
|
|
|
|
if (currsym == 1) {
|
|
for (i = 0; i < RT; i++, sample++) {
|
|
if (cwid_lastsym == 0)
|
|
outbuf[sample] = cwid_nco(freq) * cwid_keyshape[i];
|
|
else
|
|
outbuf[sample] = cwid_nco(freq);
|
|
}
|
|
for (i = 0; i < keydown; i++, sample++) {
|
|
outbuf[sample] = cwid_nco(freq);
|
|
}
|
|
}
|
|
else {
|
|
for (i = RT - 1; i >= 0; i--, sample++) {
|
|
if (cwid_lastsym == 1) {
|
|
outbuf[sample] = cwid_nco(freq) * cwid_keyshape[i];
|
|
} else {
|
|
outbuf[sample] = 0.0;
|
|
}
|
|
}
|
|
for (i = 0; i < keyup; i++, sample++) {
|
|
outbuf[sample] = 0.0;
|
|
}
|
|
}
|
|
|
|
ModulateXmtr(outbuf, cwid_symbollen);
|
|
|
|
cwid_lastsym = currsym;
|
|
}
|
|
|
|
//=====================================================================
|
|
// send_ch()
|
|
// sends a morse character and the space afterwards
|
|
//=======================================================================
|
|
|
|
void modem::cwid_send_ch(int ch)
|
|
{
|
|
int code;
|
|
|
|
// handle word space separately (7 dots spacing)
|
|
// last char already had 2 elements of inter-character spacing
|
|
|
|
if ((ch == ' ') || (ch == '\n')) {
|
|
cwid_send_symbol(0);
|
|
cwid_send_symbol(0);
|
|
cwid_send_symbol(0);
|
|
cwid_send_symbol(0);
|
|
cwid_send_symbol(0);
|
|
put_echo_char(ch);
|
|
return;
|
|
}
|
|
|
|
// convert character code to a morse representation
|
|
if ((ch < 256) && (ch >= 0)) {
|
|
code = tx_lookup(ch); //cw_tx_lookup(ch);
|
|
} else {
|
|
code = 0x04; // two extra dot spaces
|
|
}
|
|
|
|
// loop sending out binary bits of cw character
|
|
while (code > 1) {
|
|
cwid_send_symbol(code);// & 1);
|
|
code = code >> 1;
|
|
}
|
|
|
|
}
|
|
|
|
void modem::cwid_sendtext (const string& s)
|
|
{
|
|
cwid_symbollen = (int)(1.2 * samplerate / progdefaults.CWIDwpm);
|
|
RT = (int) (samplerate * 6 / 1000.0); // 6 msec risetime for CW pulse
|
|
cwid_makeshape();
|
|
cwid_lastsym = 0;
|
|
for (unsigned int i = 0; i < s.length(); i++) {
|
|
cwid_send_ch(s[i]);
|
|
}
|
|
}
|
|
|
|
void modem::cwid()
|
|
{
|
|
if (progdefaults.cwid_modes.test(mode) &&
|
|
(progdefaults.CWid == true || progdefaults.macroCWid == true)) {
|
|
string tosend = " DE ";
|
|
tosend += progdefaults.myCall;
|
|
cwid_sendtext(tosend);
|
|
progdefaults.macroCWid = false;
|
|
}
|
|
}
|
|
|
|
//=====================================================================
|
|
// transmit processing of waterfall video id
|
|
//=====================================================================
|
|
|
|
static int NUMROWS;
|
|
static int NUMCOLS;
|
|
static int TONESPACING;
|
|
static int IDSYMLEN;
|
|
static int CHARSPACE;
|
|
|
|
static bool useIDSMALL = true;
|
|
|
|
#define MAXROWS 14
|
|
#define MAXIDSYMLEN 16384
|
|
#define MAXTONES 128
|
|
#define MAXCHARS 10
|
|
|
|
struct mfntchr { char c; int byte[MAXROWS]; };
|
|
extern mfntchr idch1[]; // original id font definition
|
|
extern mfntchr idch2[]; // extended id font definition
|
|
|
|
static int symbols[MAXCHARS];
|
|
|
|
static C_FIR_filter vidfilt;
|
|
|
|
void modem::wfid_make_tones(int numchars)
|
|
{
|
|
double f, flo, fhi;
|
|
int vwidth = (numchars*NUMCOLS + (numchars-1)*CHARSPACE - 1);
|
|
f = frequency + TONESPACING * vwidth/2.0;
|
|
fhi = f + TONESPACING;
|
|
flo = fhi - (vwidth + 2) * TONESPACING;
|
|
for (int i = 1; i <= NUMCOLS * numchars; i++) {
|
|
wfid_w[i-1] = f * 2.0 * M_PI / samplerate;
|
|
f -= TONESPACING;
|
|
if ( (i > 0) && (i % NUMCOLS == 0) )
|
|
f -= TONESPACING * CHARSPACE;
|
|
}
|
|
vidfilt.init_bandpass( 1024, 1, flo/samplerate, fhi/samplerate) ;
|
|
}
|
|
|
|
void modem::wfid_send(int numchars)
|
|
{
|
|
int i, j, k;
|
|
int sym;
|
|
double val;
|
|
|
|
for (i = 0; i < IDSYMLEN; i++) {
|
|
val = 0.0;
|
|
for (k = 0; k < numchars; k++) {
|
|
sym = symbols[numchars - k - 1];
|
|
for (j = 0; j < NUMCOLS; j++) {
|
|
if (sym & 1)
|
|
val += sin(wfid_w[j + k * NUMCOLS] * i);
|
|
sym = sym >> 1;
|
|
}
|
|
}
|
|
// soft limit the signal - heuristic formulation
|
|
val = (1.0 - exp(-fabs(val)/3.0)) * (val >= 0.0 ? 1 : -1);
|
|
// band pass filter the soft limited signal
|
|
vidfilt.Irun( val, val );
|
|
wfid_outbuf[i] = val;
|
|
}
|
|
ModulateXmtr(wfid_outbuf, IDSYMLEN);
|
|
}
|
|
|
|
void modem::wfid_sendchars(string s)
|
|
{
|
|
long long int symbol;
|
|
int len = s.length();
|
|
int n[len];
|
|
int c;
|
|
wfid_make_tones(s.length());
|
|
for (int i = 0; i < len; i++) {
|
|
if (useIDSMALL) {
|
|
c = toupper(s[i]);
|
|
if (c > 'Z' || c < ' ') c = ' ';
|
|
} else {
|
|
c = s[i];
|
|
if (c > '~' || c < ' ') c = ' ';
|
|
}
|
|
n[i] = c - ' ';
|
|
}
|
|
// send rows from bottom to top so they appear to scroll down the waterfall correctly
|
|
for (int row = 0; row < NUMROWS; row++) {
|
|
symbol = 0;
|
|
for (int i = 0; i < len; i++) {
|
|
if (useIDSMALL)
|
|
symbols[i] = idch1[n[i]].byte[NUMROWS - 1 - row];
|
|
else
|
|
symbols[i] = idch2[n[i]].byte[NUMROWS - 1 - row];
|
|
}
|
|
wfid_send(len);
|
|
if (stopflag)
|
|
return;
|
|
}
|
|
}
|
|
|
|
void modem::pretone()
|
|
{
|
|
int sr = active_modem->get_samplerate();
|
|
int symlen = sr / 10;
|
|
double phaseincr = 2.0 * M_PI * active_modem->get_txfreq() / sr;
|
|
double phase = 0.0;
|
|
double outbuf[symlen];
|
|
|
|
printf("pretone symlen = %d\nduration = %4.1f\n", symlen, progdefaults.pretone);
|
|
|
|
for (int j = 0; j < symlen; j++) {
|
|
outbuf[j] = (0.5 * (1.0 - cos (M_PI * j / symlen)))*sin(phase);
|
|
phase += phaseincr;
|
|
if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI;
|
|
}
|
|
active_modem->ModulateXmtr(outbuf, symlen);
|
|
|
|
for (int i = 0; i < progdefaults.pretone * 10 - 2; i++) {
|
|
for (int j = 0; j < symlen; j++) {
|
|
outbuf[j] = sin(phase);
|
|
phase += phaseincr;
|
|
if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI;
|
|
}
|
|
active_modem->ModulateXmtr(outbuf, symlen);
|
|
}
|
|
|
|
for (int j = 0; j < symlen; j++) {
|
|
outbuf[j] = (0.5 * (1.0 - cos (M_PI * (symlen - j) / symlen)))*sin(phase);
|
|
phase += phaseincr;
|
|
if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI;
|
|
}
|
|
active_modem->ModulateXmtr(outbuf, symlen);
|
|
|
|
memset(outbuf, 0, symlen * sizeof(*outbuf));
|
|
active_modem->ModulateXmtr(outbuf, symlen);
|
|
|
|
}
|
|
|
|
void modem::wfid_text(const string& s)
|
|
{
|
|
int len = s.length();
|
|
string video = "Video text: ";
|
|
video += s;
|
|
|
|
if (progdefaults.ID_SMALL) {
|
|
NUMROWS = 7;
|
|
NUMCOLS = 5;
|
|
CHARSPACE = 2;
|
|
vidwidth = progdefaults.videowidth;
|
|
TONESPACING = 6;
|
|
IDSYMLEN = 3072;
|
|
useIDSMALL = true;
|
|
} else {
|
|
NUMROWS = 14;
|
|
NUMCOLS = 8;
|
|
CHARSPACE = 2;
|
|
vidwidth = progdefaults.videowidth;
|
|
TONESPACING = 8;
|
|
IDSYMLEN = 2560;
|
|
useIDSMALL = false;
|
|
}
|
|
if (progdefaults.vidlimit) {
|
|
if ((vidwidth * TONESPACING * (NUMCOLS + CHARSPACE)) > 500)
|
|
vidwidth = 500 / (TONESPACING * (NUMCOLS + CHARSPACE));
|
|
}
|
|
if (progdefaults.vidmodelimit) {
|
|
if ((vidwidth * TONESPACING * (NUMCOLS + CHARSPACE)) > active_modem->get_bandwidth())
|
|
vidwidth = (int)ceil(active_modem->get_bandwidth() / (TONESPACING * (NUMCOLS + CHARSPACE)));
|
|
}
|
|
|
|
put_status(video.c_str());
|
|
|
|
int numlines = 0;
|
|
string tosend;
|
|
while (numlines < len) numlines += vidwidth;
|
|
numlines -= vidwidth;
|
|
while (numlines >= 0) {
|
|
tosend = s.substr(numlines, vidwidth);
|
|
wfid_sendchars(tosend);
|
|
numlines -= vidwidth;
|
|
if (stopflag)
|
|
break;
|
|
}
|
|
// blank lines
|
|
for (int i = 0; i < vidwidth; i++) symbols[i] = 0;
|
|
wfid_send(vidwidth);
|
|
wfid_send(vidwidth);
|
|
|
|
put_status("");
|
|
}
|
|
|
|
double modem::wfid_outbuf[MAXIDSYMLEN];
|
|
double modem::wfid_w[MAXTONES];
|
|
|
|
mfntchr idch1[] = {
|
|
{ ' ', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, },
|
|
{ '!', { 0x00, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, }, },
|
|
{ '"', { 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, }, },
|
|
{ '#', { 0x00, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x00, }, },
|
|
{ '$', { 0x00, 0x0F, 0x14, 0x0E, 0x05, 0x1E, 0x00, }, },
|
|
{ '%', { 0x00, 0x19, 0x02, 0x04, 0x08, 0x13, 0x00, }, },
|
|
{ '&', { 0x00, 0x08, 0x1C, 0x0D, 0x12, 0x0F, 0x00, }, },
|
|
{ '\'', { 0x00, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, }, },
|
|
{ '(', { 0x00, 0x0C, 0x10, 0x10, 0x10, 0x0C, 0x00, }, },
|
|
{ ')', { 0x00, 0x18, 0x04, 0x04, 0x04, 0x18, 0x00, }, },
|
|
{ '*', { 0x00, 0x15, 0x0E, 0x1F, 0x0E, 0x15, 0x00, }, },
|
|
{ '+', { 0x00, 0x00, 0x04, 0x1F, 0x04, 0x00, 0x00, }, },
|
|
{ ',', { 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0x00, }, },
|
|
{ '-', { 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, }, },
|
|
{ '.', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, }, },
|
|
{ '/', { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, }, },
|
|
{ '0', { 0x00, 0x0E, 0x13, 0x15, 0x19, 0x0E, 0x00, }, },
|
|
{ '1', { 0x00, 0x0C, 0x14, 0x04, 0x04, 0x04, 0x00, }, },
|
|
{ '2', { 0x00, 0x1C, 0x02, 0x04, 0x08, 0x1F, 0x00, }, },
|
|
{ '3', { 0x00, 0x1E, 0x01, 0x06, 0x01, 0x1E, 0x00, }, },
|
|
{ '4', { 0x00, 0x12, 0x12, 0x1F, 0x02, 0x02, 0x00, }, },
|
|
{ '5', { 0x00, 0x1F, 0x10, 0x1E, 0x01, 0x1E, 0x00, }, },
|
|
{ '6', { 0x00, 0x0E, 0x10, 0x1E, 0x11, 0x0E, 0x00, }, },
|
|
{ '7', { 0x00, 0x1F, 0x01, 0x02, 0x04, 0x08, 0x00, }, },
|
|
{ '8', { 0x00, 0x0E, 0x11, 0x0E, 0x11, 0x0E, 0x00, }, },
|
|
{ '9', { 0x00, 0x0E, 0x11, 0x0F, 0x01, 0x0E, 0x00, }, },
|
|
{ ':', { 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, }, },
|
|
{ ';', { 0x00, 0x18, 0x00, 0x18, 0x08, 0x10, 0x00, }, },
|
|
{ '<', { 0x00, 0x01, 0x06, 0x18, 0x06, 0x01, 0x00, }, },
|
|
{ '=', { 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, }, },
|
|
{ '>', { 0x00, 0x10, 0x0C, 0x03, 0x0C, 0x10, 0x00, }, },
|
|
{ '?', { 0x00, 0x1C, 0x02, 0x04, 0x00, 0x04, 0x00, }, },
|
|
{ '@', { 0x00, 0x0E, 0x11, 0x16, 0x10, 0x0F, 0x00, }, },
|
|
{ 'A', { 0x00, 0x0E, 0x11, 0x1F, 0x11, 0x11, 0x00, }, },
|
|
{ 'B', { 0x00, 0x1E, 0x09, 0x0E, 0x09, 0x1E, 0x00, }, },
|
|
{ 'C', { 0x00, 0x0F, 0x10, 0x10, 0x10, 0x0F, 0x00, }, },
|
|
{ 'D', { 0x00, 0x1E, 0x11, 0x11, 0x11, 0x1E, 0x00, }, },
|
|
{ 'E', { 0x00, 0x1F, 0x10, 0x1C, 0x10, 0x1F, 0x00, }, },
|
|
{ 'F', { 0x00, 0x1F, 0x10, 0x1C, 0x10, 0x10, 0x00, }, },
|
|
{ 'G', { 0x00, 0x0F, 0x10, 0x13, 0x11, 0x0F, 0x00, }, },
|
|
{ 'H', { 0x00, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x00, }, },
|
|
{ 'I', { 0x00, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x00, }, },
|
|
{ 'J', { 0x00, 0x01, 0x01, 0x01, 0x11, 0x0E, 0x00, }, },
|
|
{ 'K', { 0x00, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x00, }, },
|
|
{ 'L', { 0x00, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, }, },
|
|
{ 'M', { 0x00, 0x11, 0x1B, 0x15, 0x11, 0x11, 0x00, }, },
|
|
{ 'N', { 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x00, }, },
|
|
{ 'O', { 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E, 0x00, }, },
|
|
{ 'P', { 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10, 0x00, }, },
|
|
{ 'Q', { 0x00, 0x0E, 0x11, 0x11, 0x15, 0x0E, 0x01, }, },
|
|
{ 'R', { 0x00, 0x1E, 0x11, 0x1E, 0x12, 0x11, 0x00, }, },
|
|
{ 'S', { 0x00, 0x0F, 0x10, 0x0E, 0x01, 0x1E, 0x00, }, },
|
|
{ 'T', { 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x00, }, },
|
|
{ 'U', { 0x00, 0x11, 0x11, 0x11, 0x11, 0x0E, 0x00, }, },
|
|
{ 'V', { 0x00, 0x11, 0x12, 0x14, 0x18, 0x10, 0x00, }, },
|
|
{ 'W', { 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A, 0x00, }, },
|
|
{ 'X', { 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00, }, },
|
|
{ 'Y', { 0x00, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x00, }, },
|
|
{ 'Z', { 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F, 0x00, }, },
|
|
};
|
|
|
|
mfntchr idch2[] = {
|
|
{' ', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'!', { 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, },
|
|
{'"', { 0x00, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'#', { 0x00, 0x50, 0x50, 0xF8, 0xF8, 0x50, 0x50, 0xF8, 0xF8, 0x50, 0x50, 0x00, 0x00, 0x00 }, },
|
|
{'$', { 0x00, 0x20, 0x20, 0x78, 0xF8, 0xA0, 0xF0, 0x78, 0x28, 0xF8, 0xF0, 0x20, 0x20, 0x00 }, },
|
|
{'%', { 0x00, 0x40, 0xE4, 0xE4, 0x4C, 0x18, 0x30, 0x60, 0xC8, 0x9C, 0x9C, 0x88, 0x00, 0x00 }, },
|
|
{'&', { 0x00, 0x30, 0x78, 0x48, 0x48, 0x70, 0xF4, 0x8C, 0x88, 0xFC, 0x74, 0x00, 0x00, 0x00 }, },
|
|
{'\'', { 0x00, 0x40, 0x40, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'(', { 0x00, 0x00, 0x20, 0x60, 0xC0, 0x80, 0x80, 0x80, 0x80, 0xC0, 0x60, 0x20, 0x00, 0x00 }, },
|
|
{')', { 0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00 }, },
|
|
{'*', { 0x00, 0x00, 0x00, 0x10, 0x10, 0xFE, 0x7C, 0x38, 0x6C, 0x44, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'+', { 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00 }, },
|
|
{',', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x40, 0xC0, 0x80 }, },
|
|
{'-', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'.', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00 }, },
|
|
{'/', { 0x00, 0x08, 0x08, 0x18, 0x10, 0x30, 0x20, 0x60, 0x40, 0xC0, 0x80, 0x80, 0x00, 0x00 }, },
|
|
{'0', { 0x00, 0x00, 0x78, 0xFC, 0x8C, 0x9C, 0xB4, 0xE4, 0xC4, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'1', { 0x00, 0x00, 0x10, 0x30, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, },
|
|
{'2', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0xFC, 0x00, 0x00 }, },
|
|
{'3', { 0x00, 0x00, 0xFC, 0xFC, 0x04, 0x0C, 0x18, 0x1C, 0x04, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'4', { 0x00, 0x00, 0x38, 0x78, 0x48, 0xC8, 0x88, 0xFC, 0xFC, 0x08, 0x08, 0x08, 0x00, 0x00 }, },
|
|
{'5', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF8, 0xFC, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x00 }, },
|
|
{'6', { 0x00, 0x00, 0x78, 0xF8, 0x80, 0x80, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'7', { 0x00, 0x00, 0xFC, 0xFC, 0x04, 0x04, 0x0C, 0x18, 0x30, 0x20, 0x20, 0x20, 0x00, 0x00 }, },
|
|
{'8', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x78, 0xFC, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'9', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0xFC, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, },
|
|
{':', { 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{';', { 0x00, 0x60, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x00 }, },
|
|
{'<', { 0x00, 0x00, 0x08, 0x18, 0x30, 0x60, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x08, 0x00, 0x00 }, },
|
|
{'=', { 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'>', { 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00 }, },
|
|
{'?', { 0x00, 0x00, 0x70, 0xF8, 0x88, 0x08, 0x18, 0x30, 0x20, 0x00, 0x20, 0x20, 0x00, 0x00 }, },
|
|
{'@', { 0x00, 0x00, 0x7C, 0xFE, 0x82, 0x82, 0xB2, 0xBE, 0xBC, 0x80, 0xFC, 0x7C, 0x00, 0x00 }, },
|
|
{'A', { 0x00, 0x00, 0x30, 0x78, 0xCC, 0x84, 0x84, 0xFC, 0xFC, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'B', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xF8, 0xF8, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x00 }, },
|
|
{'C', { 0x00, 0x00, 0x38, 0x7C, 0xC4, 0x80, 0x80, 0x80, 0x80, 0xC4, 0x7C, 0x38, 0x00, 0x00 }, },
|
|
{'D', { 0x00, 0x00, 0xF0, 0xF8, 0x8C, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xF8, 0xF0, 0x00, 0x00 }, },
|
|
{'E', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF0, 0xF0, 0x80, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
|
|
{'F', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF0, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
|
|
{'G', { 0x00, 0x00, 0x3C, 0x7C, 0xC0, 0x80, 0x8C, 0x8C, 0x84, 0xC4, 0x7C, 0x38, 0x00, 0x00 }, },
|
|
{'H', { 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0xFC, 0xFC, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'I', { 0x00, 0x00, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
|
|
{'J', { 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'K', { 0x00, 0x00, 0x84, 0x84, 0x8C, 0x98, 0xF0, 0xF0, 0x98, 0x8C, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'L', { 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
|
|
{'M', { 0x00, 0x00, 0x82, 0xC6, 0xEE, 0xBA, 0x92, 0x82, 0x82, 0x82, 0x82, 0x82, 0x00, 0x00 }, },
|
|
{'N', { 0x00, 0x00, 0x84, 0xC4, 0xE4, 0xB4, 0x9C, 0x8C, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'O', { 0x00, 0x00, 0x30, 0x78, 0xCC, 0x84, 0x84, 0x84, 0x84, 0xCC, 0x78, 0x30, 0x00, 0x00 }, },
|
|
{'P', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0xF8, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
|
|
{'Q', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x84, 0x84, 0x94, 0x94, 0xFC, 0x78, 0x08, 0x08 }, },
|
|
{'R', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0xF8, 0x88, 0x8C, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'S', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x80, 0xF8, 0x7C, 0x04, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'T', { 0x00, 0x00, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }, },
|
|
{'U', { 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'V', { 0x00, 0x00, 0x82, 0x82, 0x82, 0xC6, 0x44, 0x6C, 0x28, 0x38, 0x10, 0x10, 0x00, 0x00 }, },
|
|
{'W', { 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0x82, 0x92, 0x92, 0x92, 0xFE, 0x6C, 0x00, 0x00 }, },
|
|
{'X', { 0x00, 0x00, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x82, 0x82, 0x00, 0x00 }, },
|
|
{'Y', { 0x00, 0x00, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, },
|
|
{'Z', { 0x00, 0x00, 0xFC, 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
|
|
{'[', { 0x00, 0x00, 0xE0, 0xE0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xE0, 0xE0, 0x00, 0x00 }, },
|
|
{'\\', { 0x00, 0x80, 0x80, 0xC0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x08, 0x00, 0x00 }, },
|
|
{']', { 0x00, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x00, 0x00 }, },
|
|
{'^', { 0x00, 0x20, 0x20, 0x70, 0x50, 0xD8, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'_', { 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00 }, },
|
|
{'`', { 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
|
|
{'a', { 0x00, 0x00, 0x00, 0x00, 0x78, 0x7C, 0x04, 0x7C, 0xFC, 0x84, 0xFC, 0x7C, 0x00, 0x00 }, },
|
|
{'b', { 0x00, 0x00, 0x80, 0x80, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x00 }, },
|
|
{'c', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xF8, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x78, 0x00, 0x00 }, },
|
|
{'d', { 0x00, 0x00, 0x04, 0x04, 0x74, 0xFC, 0x8C, 0x84, 0x84, 0x84, 0xFC, 0x7C, 0x00, 0x00 }, },
|
|
{'e', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xFC, 0x84, 0xFC, 0xFC, 0x80, 0xF8, 0x78, 0x00, 0x00 }, },
|
|
{'f', { 0x00, 0x00, 0x3C, 0x7C, 0x40, 0x40, 0xF8, 0xF8, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, },
|
|
{'g', { 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x7C, 0x78, 0x00 }, },
|
|
{'h', { 0x00, 0x00, 0x80, 0x80, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'i', { 0x00, 0x20, 0x20, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
|
|
{'j', { 0x00, 0x08, 0x08, 0x00, 0x38, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x88, 0xF8, 0x70 }, },
|
|
{'k', { 0x00, 0x00, 0x80, 0x88, 0x98, 0xB0, 0xE0, 0xE0, 0xB0, 0x98, 0x88, 0x88, 0x00, 0x00 }, },
|
|
{'l', { 0x00, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
|
|
{'m', { 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0x92, 0x92, 0x82, 0x82, 0x82, 0x82, 0x00, 0x00 }, },
|
|
{'n', { 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
|
|
{'o', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
|
|
{'p', { 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xC4, 0xFC, 0xB8, 0x80, 0x80, 0x80 }, },
|
|
{'q', { 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x04, 0x04 }, },
|
|
{'r', { 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFC, 0xC4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
|
|
{'s', { 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x80, 0xF8, 0x7C, 0x04, 0xFC, 0xF8, 0x00, 0x00 }, },
|
|
{'t', { 0x00, 0x00, 0x40, 0x40, 0xF0, 0xF0, 0x40, 0x40, 0x40, 0x40, 0x78, 0x38, 0x00, 0x00 }, },
|
|
{'u', { 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x00, 0x00 }, },
|
|
{'v', { 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00 }, },
|
|
{'w', { 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x92, 0x92, 0x92, 0xFE, 0x6C, 0x00, 0x00 }, },
|
|
{'x', { 0x00, 0x00, 0x00, 0x00, 0x82, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x82, 0x00, 0x00 }, },
|
|
{'y', { 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x7C, 0x78 }, },
|
|
{'z', { 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0xFC, 0x00, 0x00 }, },
|
|
{'{', { 0x00, 0x20, 0x60, 0x40, 0x40, 0x40, 0xC0, 0xC0, 0x40, 0x40, 0x40, 0x60, 0x20, 0x00 }, },
|
|
{'|', { 0x00, 0x80, 0x80, 0xC0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x08, 0x00, 0x00 }, },
|
|
{'}', { 0x00, 0x80, 0xC0, 0x40, 0x40, 0x40, 0x60, 0x60, 0x40, 0x40, 0x40, 0xC0, 0x80, 0x00 }, },
|
|
{'~', { 0x00, 0x98, 0xFC, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
|
|
};
|