Wednesday 11 January 2017

BASIC Tech Group - MyNews 18 - HELL_S/MT_5x7

One of the interesting things to do when operating narrow band is to use the QRSS frequencies which normally transmit slow morse or DFCW (dual frequency CW), to instead transmit ASCII characters on a set of 8 bit mapped frequencies, this sends a bit mapped font character.

This results in a display at QRSS3 speeds as shown in Argo software:

Screen Shot 2017 01 11 at 16 16 13

I am using an SDR receiver (this one is the Elektor SDR) tuned to 7050kHz centre frequency, with the IQ outputs going to a Startech A/D and on to my iMac computer. I am running the HDSDR software (under Wine) tuned to 7038.7kHz USB, so giving an audio tone of 1500kHz displayed in Argo

IMG 0539

The transmitter is an Arduino UNO programming an Si5351 synthesiser module. This includes an RTC and TX is made every 5 mins

IMG 0538

And here's couple more photos using the SDR_40M "Concept" SDR and Mac native DSP Radio software, audio is sent from the Radio to Argo using Soundflower software (recently updated for MacOS 10 and much improved)

Screen Shot 2017 01 15 at 15 37 48

Updated message after I got my Full Licence...

Screen Shot 2017 01 15 at 15 43 40

The code below is for transmission of an embedded line "CQ M0IFA IO92HF" (replace this with your text, max 16 characters if you want it to display on the LCD screen). The font here is a normal right-way-up font.

Code - updated 5-2-17

// HELL_S/MT_5x7 Sequential MultiTone Hellschreiber
// 5x7 font, normal
// Si5351 correction for ARDUINO COURSE BB, may need adustment
// Si5351 I2C bus
// SDA = A4
// SCL = A5
// LCD I2C bus (16x2)
// SDA = A4
// SCL = A5
// rotary encoder pins
// DT = 2
// CLK = 3
// SW = 4

// Si5351, LCDI2C libraries
#include "si5351.h"
#include "LiquidCrystal_I2C.h"

// LCD 0x27 or 0x3F
//#define LCDADDR 0x3F
#define LCDADDR 0x27
#define LCDCOLS 16
#define LCDROWS 2

// frequencies, base and shift (cHz)
#define BASEFREQ 707000000  // 40m, freqs 7077, 10132, 14070kHz
#define SHIFT 200           // 2Hz shift, 14Hz total BW

//each pixel 300ms, chargap 5pix = 1500ms
#define PIXTIME 300
#define CHARGAP 1500

// table of glyphs 0-9 & A-Z, cols b0-b7
byte glyphs[][6] = {
  {' ', 0x00, 0x00, 0x00, 0x00, 0x00, },
  {'/', 0x04, 0x08, 0x10, 0x20, 0x40, },
  {'0', 0x7C, 0x8A, 0x92, 0xA2, 0x7C, },
  {'1', 0x00, 0x42, 0xFE, 0x02, 0x00, },
  {'2', 0x42, 0x86, 0x8A, 0x92, 0x62, },
  {'3', 0x84, 0x82, 0xA2, 0xD2, 0x8C, },
  {'4', 0x18, 0x28, 0x48, 0xFE, 0x08, },
  {'5', 0xE4, 0xA2, 0xA2, 0xA2, 0x9C, },
  {'6', 0x3C, 0x52, 0x92, 0x92, 0x0C, },
  {'7', 0x80, 0x8E, 0x90, 0xA0, 0xC0, },
  {'8', 0x6C, 0x92, 0x92, 0x92, 0x6C, },
  {'9', 0x60, 0x92, 0x92, 0x94, 0x78, },
  {'A', 0x7E, 0x88, 0x88, 0x88, 0x7E, },
  {'B', 0xFE, 0x92, 0x92, 0x92, 0x6C, },
  {'C', 0x7C, 0x82, 0x82, 0x82, 0x44, },
  {'D', 0xFE, 0x82, 0x82, 0x44, 0x38, },
  {'E', 0xFE, 0x92, 0x92, 0x92, 0x82, },
  {'F', 0xFE, 0x90, 0x90, 0x90, 0x80, },
  {'G', 0x7C, 0x82, 0x92, 0x92, 0x5E, },
  {'H', 0xFE, 0x10, 0x10, 0x10, 0xFE, },
  {'I', 0x00, 0x82, 0xFE, 0x82, 0x00, },
  {'J', 0x04, 0x02, 0x82, 0xFC, 0x80, },
  {'K', 0xFE, 0x10, 0x28, 0x44, 0x82, },
  {'L', 0xFE, 0x02, 0x02, 0x02, 0x02, },
  {'M', 0xFE, 0x40, 0x30, 0x40, 0xFE, },
  {'N', 0xFE, 0x20, 0x10, 0x08, 0xFE, },
  {'O', 0x7C, 0x82, 0x82, 0x82, 0x7C, },
  {'P', 0xFE, 0x90, 0x90, 0x90, 0x60, },
  {'Q', 0x7C, 0x82, 0x8A, 0x84, 0x7A, },
  {'R', 0xFE, 0x90, 0x98, 0x94, 0x62, },
  {'S', 0x62, 0x92, 0x92, 0x92, 0x8C, },
  {'T', 0x80, 0x80, 0xFE, 0x80, 0x80, },
  {'U', 0xFC, 0x02, 0x02, 0x02, 0xFC, },
  {'V', 0xF8, 0x04, 0x02, 0x04, 0xF8, },
  {'W', 0xFC, 0x02, 0x1C, 0x02, 0xFC, },
  {'X', 0xC6, 0x28, 0x10, 0x28, 0xC6, },
  {'Y', 0xE0, 0x10, 0x0E, 0x10, 0xE0, },
  {'Z', 0x86, 0x8A, 0x92, 0xA2, 0xC2, },
};

// calc number of glyphs in glyphs table
#define NGLYPHS ((sizeof glyphs) / (sizeof glyphs[0]))

// freq variable
uint64_t freq = BASEFREQ; // base or bottom freq of pixels

// message to send
char msg[] = "TESTING M0IFA"; // max 16 char for LCD display, can transmit longer

// dds object
Si5351 dds;

// LCD object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

void setup() {
  // init lcd & I2C
  lcd.begin();

  // init dds
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);

  // set drive output 8mA (about 4dBm into 50R)
  dds.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);

  // disable all outputs
  dds.output_enable(SI5351_CLK0, 0);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 0);

  // initial display
  dispMsg(0, 0, " HELLSCHREIBER    ");        // title & clear line
  Serial.println("START 1 sec");
  delay(1000);
}

void loop() {
  dispMsg(0, 0, "                 ");        // clear title
  dispMsg(0, 0, msg);                        // disp msg
  dispFreq(1, 1, freq, 1);                   // display freq
  dispMsg(11, 1, " USB ");

  sendMsg(msg);

  dispMsg(11, 1, "     ");
  dispMsg(0, 0, " HELLSCHREIBER    ");        // title & clear line
  dispMsg(0, 1, "                  ");

  delay(30000);                              // next 30sec delay
}

// take glyphs of message and send
void sendMsg(char *m) {
  int c, i, j;                                               // c scans msg, i & j scan the glyphs & rows
  int pix;                                                   // pix scans pixel cols in glyph

  c = 0;
  while (m[c] != '\0') {                                     // scan until end of msg
    for (i = 0; i < NGLYPHS; i++) {                          // scan glyphs, always scan all rows to preserve timing
      if (m[c] == glyphs[i][0]) {                            // if m[c] found at [i][0]
        for (j = 1; j <= 5; j++) {                           // read glyphs[j][1] to [j][5]
          for (pix = 1; pix <= 7; pix++) {                   // font cols b1-b7 for USB
            if (bitRead(glyphs[i][j], pix) == 1) {           // send pix
              outFreq(freq + (pix * SHIFT), PIXTIME);        // calc output freq
            }
            else {
              delay(PIXTIME);
            }
          }
        }
        delay(CHARGAP);
      }
    }
    c++;
  }
}

// output f for t ms
void outFreq(uint64_t f, uint16_t t) {
  dds.set_freq(f, SI5351_CLK0);                   // output freq
  dds.output_enable(SI5351_CLK0, 1);
  delay(t);
  dds.output_enable(SI5351_CLK0, 0);
}

// display freq in kHz,col c, row r, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint64_t f, uint8_t d)
{
  lcd.setCursor(c, r); // clear last freq display
  lcd.print((float)f / 100000, d); // convert to float for print function
  lcd.print("kHz  "); // + trailing spaces to clear previous display
}

// display a number at col c, row r
void dispNum(uint8_t c, uint8_t r, uint16_t n)
{
  lcd.setCursor(c, r);
  lcd.print(n);
}

// display char msg at col c, row r
void dispMsg(uint8_t c, uint8_t r, char *m)
{
  lcd.setCursor(c, r);
  lcd.print(m);
}


No comments: