pull/2/head
lightaprs 2019-08-06 19:50:49 +03:00
rodzic 0617bdf236
commit dfb42e2c48
122 zmienionych plików z 19934 dodań i 2 usunięć

3
.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,3 @@
### macOS ###
# General
.DS_Store

Wyświetl plik

@ -0,0 +1,988 @@
#include <Arduino.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <GEOFENCE.h> // Modified version of https://github.com/TomasTT7/TT7F-Float-Tracker/blob/master/Software/ARM_GEOFENCE.c
#include <LibAPRS.h> //Modified version of https://github.com/markqvist/LibAPRS
#include <TinyGPS++.h> //https://github.com/mikalhart/TinyGPSPlus
#include <LowPower.h> //https://github.com/rocketscream/Low-Power
#include <Adafruit_BMP085.h>//https://github.com/adafruit/Adafruit-BMP085-Library
#include <si5351.h> //https://github.com/etherkit/Si5351Arduino
#include <JTEncode.h> //https://github.com/etherkit/JTEncode (JT65/JT9/JT4/FT8/WSPR/FSQ Encoder Library)
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
#include <TimerThree.h> //https://github.com/PaulStoffregen/TimerThree/
#define RfPDPin 19
#define GpsVccPin 18
#define RfPwrHLPin 21
#define RfPttPin 20
#define SiVccPin 0
#define BattPin A2
#define PIN_DRA_RX 22
#define PIN_DRA_TX 23
#define ADC_REFERENCE REF_3V3
#define OPEN_SQUELCH false
#define GpsON digitalWrite(GpsVccPin, LOW)//PNP
#define GpsOFF digitalWrite(GpsVccPin, HIGH)
#define RfON digitalWrite(RfPDPin, HIGH)
#define RfOFF digitalWrite(RfPDPin, LOW)
#define RfPwrHigh pinMode(RfPwrHLPin, INPUT)
#define RfPwrLow pinMode(RfPwrHLPin, OUTPUT);digitalWrite(RfPwrHLPin, LOW)
#define RfPttON digitalWrite(RfPttPin, HIGH)//NPN
#define RfPttOFF digitalWrite(RfPttPin, LOW)
#define SiON digitalWrite(SiVccPin, HIGH)//NPN
#define SiOFF digitalWrite(SiVccPin, LOW)
#define AprsPinInput pinMode(12,INPUT);pinMode(13,INPUT);pinMode(14,INPUT);pinMode(15,INPUT)
#define AprsPinOutput pinMode(12,OUTPUT);pinMode(13,OUTPUT);pinMode(14,OUTPUT);pinMode(15,OUTPUT)
//#define DEVMODE // Development mode. Uncomment to enable for debugging.
//****************************** APRS CONFIG **********************************
char CallSign[7]="TA2MUN"; //DO NOT FORGET TO CHANGE YOUR CALLSIGN
int8_t CallNumber=11; //SSID http://www.aprs.org/aprs11/SSIDs.txt
char Symbol='O'; // '/O' for balloon, '/>' for car, for more info : http://www.aprs.org/symbols/symbols-new.txt
bool alternateSymbolTable = false ; //false = '/' , true = '\'
char comment[50] = "http://www.lightaprs.com"; // Max 50 char
char StatusMessage[50] = "LightAPRS-W by TA9OHC & TA2MUN";
//*****************************************************************************
uint16_t BeaconWait=50; //seconds sleep for next beacon (HF or VHF). This is optimized value, do not change this if possible.
uint16_t BattWait=60; //seconds sleep if super capacitors/batteries are below BattMin (important if power source is solar panel)
float BattMin=4.5; // min Volts to wake up.
float DraHighVolt=6.0;// min Volts for radio module (DRA818V) to transmit (TX) 1 Watt, below this transmit 0.5 Watt.
float GpsMinVolt=4.0; //min Volts for GPS to wake up. (important if power source is solar panel)
float WsprBattMin=4.5; //min Volts for HF mradio module to transmit (TX) ~10 mW
//****************************** HF CONFIG *************************************
char hf_call[7] = "TA2MUN"; //DO NOT FORGET TO CHANGE YOUR CALLSIGN
//#define WSPR_DEFAULT_FREQ 10140200UL //30m band
#define WSPR_DEFAULT_FREQ 14097200UL //20m band
//#define WSPR_DEFAULT_FREQ 18106100UL //17M band
//#define WSPR_DEFAULT_FREQ 21096100UL //15m band
//#define WSPR_DEFAULT_FREQ 24926100UL //12M band
//#define WSPR_DEFAULT_FREQ 28126100UL //10m band
//for all bands -> http://wsprnet.org/drupal/node/7352
// Supported modes, default HF mode is WSPR
enum mode {MODE_JT9, MODE_JT65, MODE_JT4, MODE_WSPR, MODE_FSQ_2, MODE_FSQ_3,
MODE_FSQ_4_5, MODE_FSQ_6, MODE_FT8};
enum mode cur_mode = MODE_WSPR; //default HF mode
//supported other modes freq for 20m
#define JT9_DEFAULT_FREQ 14078700UL
#define FT8_DEFAULT_FREQ 14075000UL
#define JT65_DEFAULT_FREQ 14078300UL
#define JT4_DEFAULT_FREQ 14078500UL
#define FSQ_DEFAULT_FREQ 7105350UL // Base freq is 1350 Hz higher than dial freq in USB
//*******************************************************************************
//****************************** APRS SETTINGS *********************************
//do not change WIDE path settings below if you don't know what you are doing :)
uint8_t Wide1=1; // 1 for WIDE1-1 path
uint8_t Wide2=1; // 1 for WIDE2-1 path
/**
Airborne stations above a few thousand feet should ideally use NO path at all, or at the maximum just WIDE2-1 alone.
Due to their extended transmit range due to elevation, multiple digipeater hops are not required by airborne stations.
Multi-hop paths just add needless congestion on the shared APRS channel in areas hundreds of miles away from the aircraft's own location.
NEVER use WIDE1-1 in an airborne path, since this can potentially trigger hundreds of home stations simultaneously over a radius of 150-200 miles.
*/
uint8_t pathSize=2; // 2 for WIDE1-N,WIDE2-N ; 1 for WIDE2-N
boolean autoPathSizeHighAlt = true; //force path to WIDE2-N only for high altitude (airborne) beaconing (over 1.000 meters (3.280 feet))
boolean beaconViaARISS = false; //there are no iGates in some regions (such as North Africa, Oceans, etc) so try to beacon via ARISS (International Space Station) https://www.amsat.org/amateur-radio-on-the-iss/
boolean radioSetup = false;
boolean aliveStatus = true; //for tx status message on first wake-up just once.
static char telemetry_buff[100];// telemetry buffer
uint16_t TxCount = 1; //increase +1 after every APRS transmission
//*******************************************************************************
//****************************** HF SETTINGS *********************************
#define JT9_TONE_SPACING 174 // ~1.74 Hz
#define JT65_TONE_SPACING 269 // ~2.69 Hz
#define JT4_TONE_SPACING 437 // ~4.37 Hz
#define WSPR_TONE_SPACING 146 // ~1.46 Hz
#define FSQ_TONE_SPACING 879 // ~8.79 Hz
#define FT8_TONE_SPACING 625 // ~6.25 Hz
#define JT9_DELAY 576 // Delay value for JT9-1
#define JT65_DELAY 371 // Delay in ms for JT65A
#define JT4_DELAY 229 // Delay value for JT4A
#define WSPR_DELAY 683 // Delay value for WSPR
#define FSQ_2_DELAY 500 // Delay value for 2 baud FSQ
#define FSQ_3_DELAY 333 // Delay value for 3 baud FSQ
#define FSQ_4_5_DELAY 222 // Delay value for 4.5 baud FSQ
#define FSQ_6_DELAY 167 // Delay value for 6 baud FSQ
#define FT8_DELAY 159 // Delay value for FT8
#define CORRECTION -3700 // Change this for your ref osc
// Global variables
unsigned long hf_freq;
char hf_message[13] = "NOCALL AA00";//for non WSPR modes, you don't have to change this, updated by hf_call and GPS location
char hf_loc[] = "AA00"; //for WSPR, updated by GPS location. You don't have to change this.
uint8_t dbm = 10;
uint8_t tx_buffer[255];
uint8_t symbol_count;
uint16_t tone_delay, tone_spacing;
volatile bool proceed = false;
boolean HFSent = false;
//*******************************************************************************
//****************************** GPS SETTINGS *********************************
int16_t GpsResetTime=600; // timeout for reset if GPS is not fixed
// GEOFENCE
uint32_t GEOFENCE_APRS_frequency = 144390000; //default frequency before geofencing. This variable will be updated based on GPS location.
uint32_t GEOFENCE_no_tx = 0;
boolean GpsFirstFix=false; //do not change this
boolean ublox_high_alt_mode_enabled = false; //do not change this
int16_t GpsInvalidTime=0; //do not change this
//********************************************************************************
Si5351 si5351(0x60);
TinyGPSPlus gps;
Adafruit_BMP085 bmp;
JTEncode jtencode;
void setup() {
wdt_enable(WDTO_8S);
analogReference(INTERNAL2V56);
pinMode(RfPDPin, OUTPUT);
pinMode(GpsVccPin, OUTPUT);
pinMode(RfPwrHLPin, OUTPUT);
pinMode(RfPttPin, OUTPUT);
pinMode(BattPin, INPUT);
pinMode(PIN_DRA_TX, INPUT);
pinMode(SiVccPin, OUTPUT);
RfOFF;
GpsOFF;
RfPwrLow;
RfPttOFF;
SiOFF;
AprsPinInput;
Serial.begin(57600); //Arduino serial
Serial1.begin(9600); //GPS serial
#if defined(DEVMODE)
Serial.println(F("Start"));
#endif
APRS_init(ADC_REFERENCE, OPEN_SQUELCH);
APRS_setCallsign(CallSign,CallNumber);
APRS_setDestination("APLIGA", 0);
APRS_setMessageDestination("APLIGA", 0);
APRS_setPath1("WIDE1", Wide1);
APRS_setPath2("WIDE2", Wide2);
APRS_useAlternateSymbolTable(alternateSymbolTable);
APRS_setSymbol(Symbol);
APRS_setPathSize(pathSize);
AprsPinInput;
bmp.begin();
}
void loop() {
wdt_reset();
if (readBatt() > BattMin) {
if (aliveStatus) {
#if defined(DEVMODE)
Serial.println(F("Sending"));
#endif
sendStatus();
#if defined(DEVMODE)
Serial.println(F("Status sent"));
#endif
aliveStatus = false;
while (readBatt() < BattMin) {
sleepSeconds(BattWait);
}
}
updateGpsData(1000);
gpsDebug();
if(gps.location.isValid() && gps.location.age()<1000){
GpsInvalidTime=0;
}else{
GpsInvalidTime++;
if(GpsInvalidTime > GpsResetTime){
GpsOFF;
ublox_high_alt_mode_enabled = false; //gps sleep mode resets high altitude mode.
wdt_reset();
delay(1000);
GpsON;
GpsInvalidTime=0;
}
}
if ((gps.location.age() < 1000 || gps.location.isUpdated()) && gps.location.isValid()) {
if (gps.satellites.isValid() && gps.satellites.value() >= 3) {
GpsOFF;
GpsFirstFix = true;
ublox_high_alt_mode_enabled = false; //gps sleep mode resets high altitude mode.
#if defined(DEVMODE)
Serial.println(F("Before WSPR Check"));
#endif
// preparations for HF starts one minute before TX time at minute 3, 7, 13, 17, 23, 27, 33, 37, 43, 47, 53 or 57. No APRS TX during this period...
if (readBatt() > WsprBattMin && timeStatus() == timeSet && ((minute() % 10 == 3) || (minute() % 10 == 7)) ) {
GridLocator(hf_loc, gps.location.lat(), gps.location.lng());
sprintf(hf_message,"%s %s",hf_call,hf_loc);
#if defined(DEVMODE)
Serial.println(F("Digital HF Mode Prepearing"));
Serial.println(hf_loc);
#endif
HFSent = false;
//HF transmission starts at minute 4, 8, 14, 18, 24, 28, 34, 38, 44, 48, 54 or 58
while (((minute() % 10 != 4) || (minute() % 10 != 8)) && second() != 0) {
wdt_reset();
}
#if defined(DEVMODE)
Serial.println(F("Digital HF Mode Sending"));
#endif
encode();
HFSent = true;
#if defined(DEVMODE)
Serial.println(F("Digital HF Mode Sent"));
#endif
} else {
updatePosition();
updateTelemetry();
//APRS frequency isn't the same for the whole world. (for pico balloon only)
if (!radioSetup || TxCount == 200) {
configureFreqbyLocation();
}
if(autoPathSizeHighAlt && gps.altitude.feet()>3000){
//force to use high altitude settings (WIDE2-n)
APRS_setPathSize(1);
} else {
//use defualt settings
APRS_setPathSize(pathSize);
}
//in some countries Airborne APRS is not allowed. (for pico balloon only)
if (isAirborneAPRSAllowed()) {
sendLocation();
}
freeMem();
Serial.flush();
//If two minutes time slot is close, sleep less than default.
if (timeStatus() == timeSet && ((minute() % 10 == 2) || (minute() % 10 == 6))){
sleepSeconds(60 - second());
} else {
sleepSeconds(BeaconWait);
}
}
} else {
#if defined(DEVMODE)
Serial.println(F("Not enough sattelites"));
#endif
}
}
} else {
sleepSeconds(BattWait);
}
}
void aprs_msg_callback(struct AX25Msg *msg) {
//do not remove this function, necessary for LibAPRS.
}
void sleepSeconds(int sec) {
if (GpsFirstFix){//sleep gps after first fix
GpsOFF;
ublox_high_alt_mode_enabled = false;
}
RfOFF;
RfPttOFF;
SiOFF;
#if defined(DEVMODE)
Serial.flush();
#endif
wdt_disable();
for (int i = 0; i < sec; i++) {
if (readBatt() < GpsMinVolt){
GpsOFF;
ublox_high_alt_mode_enabled = false;
}
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_ON);
}
wdt_enable(WDTO_8S);
wdt_reset();
}
boolean isAirborneAPRSAllowed() {
float tempLat = gps.location.lat();
float tempLong = gps.location.lng();
GEOFENCE_position(tempLat, tempLong);
boolean airborne = true;
if (GEOFENCE_no_tx == 1) {
airborne = false;
}
return airborne;
}
boolean inARISSGeoFence(float tempLat, float tempLong) {
boolean ariss = false;
//North Africa
if(tempLat>0 && tempLat<32 && tempLong>0 && tempLong<32){ariss = true;}
//North Pacific
if(tempLat>28 && tempLat<50 && tempLong>-180 && tempLong<-130){ariss = true;}
//North Atlantic
if(tempLat>25 && tempLat<42 && tempLong>-60 && tempLong<-33){ariss = true;}
return ariss;
}
void configureFreqbyLocation() {
float tempLat = gps.location.lat();
float tempLong = gps.location.lng();
if(beaconViaARISS && inARISSGeoFence(tempLat, tempLong)) {
APRS_setPath1("ARISS", Wide1);
APRS_setPath2("WIDE2", Wide2);
APRS_setPathSize(2);
configDra818("145.8250");
} else {
GEOFENCE_position(tempLat,tempLong);
float dividedFreq = GEOFENCE_APRS_frequency / 1000000.f;
char aprsFreq_buff[8];
dtostrf(dividedFreq, 8, 4, aprsFreq_buff);
configDra818(aprsFreq_buff);
}
radioSetup = true;
}
byte configDra818(char *freq)
{
SoftwareSerial Serial_dra(PIN_DRA_RX, PIN_DRA_TX);
Serial_dra.begin(9600);
RfON;
char ack[3];
int n;
delay(2000);
char cmd[50];
#if defined(DEVMODE)
Serial.println(F("Config DRA.."));
#endif
sprintf(cmd, "AT+DMOSETGROUP=0,%s,%s,0000,4,0000", freq, freq);
Serial_dra.println(cmd);
ack[2] = 0;
while (ack[2] != 0xa)
{
if (Serial_dra.available() > 0) {
ack[0] = ack[1];
ack[1] = ack[2];
ack[2] = Serial_dra.read();
}
}
Serial_dra.end();
RfOFF;
pinMode(PIN_DRA_TX, INPUT);
#if defined(DEVMODE)
if (ack[0] == 0x30) Serial.println(F("Frequency updated...")); else Serial.println(F("Frequency update error!"));
#endif
return (ack[0] == 0x30) ? 1 : 0;
}
void updatePosition() {
// Convert and set latitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[S,N].
char latStr[10];
int temp = 0;
double d_lat = gps.location.lat();
double dm_lat = 0.0;
if (d_lat < 0.0) {
temp = -(int)d_lat;
dm_lat = temp * 100.0 - (d_lat + temp) * 60.0;
} else {
temp = (int)d_lat;
dm_lat = temp * 100 + (d_lat - temp) * 60.0;
}
dtostrf(dm_lat, 7, 2, latStr);
if (dm_lat < 1000) {
latStr[0] = '0';
}
if (d_lat >= 0.0) {
latStr[7] = 'N';
} else {
latStr[7] = 'S';
}
APRS_setLat(latStr);
// Convert and set longitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[E,W].
char lonStr[10];
double d_lon = gps.location.lng();
double dm_lon = 0.0;
if (d_lon < 0.0) {
temp = -(int)d_lon;
dm_lon = temp * 100.0 - (d_lon + temp) * 60.0;
} else {
temp = (int)d_lon;
dm_lon = temp * 100 + (d_lon - temp) * 60.0;
}
dtostrf(dm_lon, 8, 2, lonStr);
if (dm_lon < 10000) {
lonStr[0] = '0';
}
if (dm_lon < 1000) {
lonStr[1] = '0';
}
if (d_lon >= 0.0) {
lonStr[8] = 'E';
} else {
lonStr[8] = 'W';
}
APRS_setLon(lonStr);
}
void updateTelemetry() {
sprintf(telemetry_buff, "%03d", gps.course.isValid() ? (int)gps.course.deg() : 0);
telemetry_buff[3] += '/';
sprintf(telemetry_buff + 4, "%03d", gps.speed.isValid() ? (int)gps.speed.knots() : 0);
telemetry_buff[7] = '/';
telemetry_buff[8] = 'A';
telemetry_buff[9] = '=';
sprintf(telemetry_buff + 10, "%06lu", (long)gps.altitude.feet());
telemetry_buff[16] = ' ';
sprintf(telemetry_buff + 17, "%03d", TxCount);
telemetry_buff[20] = 'T';
telemetry_buff[21] = 'x';
telemetry_buff[22] = 'C';
telemetry_buff[23] = ' '; float tempC = bmp.readTemperature();//-21.4;//
dtostrf(tempC, 6, 2, telemetry_buff + 24);
telemetry_buff[30] = 'C';
telemetry_buff[31] = ' '; float pressure = bmp.readPressure() / 100.0; //Pa to hPa
dtostrf(pressure, 7, 2, telemetry_buff + 32);
telemetry_buff[39] = 'h';
telemetry_buff[40] = 'P';
telemetry_buff[41] = 'a';
telemetry_buff[42] = ' ';
dtostrf(readBatt(), 5, 2, telemetry_buff + 43);
telemetry_buff[48] = 'V';
telemetry_buff[49] = ' ';
sprintf(telemetry_buff + 50, "%02d", gps.satellites.isValid() ? (int)gps.satellites.value() : 0);
telemetry_buff[52] = 'S';
telemetry_buff[53] = ' ';
sprintf(telemetry_buff + 54, "%s", comment);
#if defined(DEVMODE)
Serial.println(telemetry_buff);
#endif
}
void sendLocation() {
#if defined(DEVMODE)
Serial.println(F("Location sending with comment"));
#endif
if ((readBatt() > DraHighVolt) && (readBatt() < 10)) RfPwrHigh; //DRA Power 1 Watt
else RfPwrLow; //DRA Power 0.5 Watt
int hh = gps.time.hour();
int mm = gps.time.minute();
int ss = gps.time.second();
char timestamp_buff[7];
sprintf(timestamp_buff, "%02d", gps.time.isValid() ? (int)gps.time.hour() : 0);
sprintf(timestamp_buff + 2, "%02d", gps.time.isValid() ? (int)gps.time.minute() : 0);
sprintf(timestamp_buff + 4, "%02d", gps.time.isValid() ? (int)gps.time.second() : 0);
timestamp_buff[6] = 'h';
AprsPinOutput;
RfON;
delay(2000);
RfPttON;
delay(1000);
APRS_sendLocWtTmStmp(telemetry_buff, strlen(telemetry_buff), timestamp_buff);
delay(50);
while (digitalRead(1)) {;} //LibAprs TX Led pin PB1
delay(50);
RfPttOFF;
RfOFF;
AprsPinInput;
#if defined(DEVMODE)
Serial.println(F("Location sent with comment"));
#endif
TxCount++;
}
void sendStatus() {
if ((readBatt() > DraHighVolt) && (readBatt() < 10)) RfPwrHigh; //DRA Power 1 Watt
else RfPwrLow; //DRA Power 0.5 Watt
AprsPinOutput;
RfON;
delay(2000);
RfPttON;
delay(1000);
APRS_sendStatus(StatusMessage, strlen(StatusMessage));
delay(50);
while (digitalRead(1)) {;} //LibAprs TX Led pin PB1
delay(50);
RfPttOFF;
RfOFF;
AprsPinInput;
#if defined(DEVMODE)
Serial.println(F("Status sent"));
#endif
TxCount++;
}
static void updateGpsData(int ms)
{
GpsON;
if(!ublox_high_alt_mode_enabled){
//enable ublox high altitude mode
delay(100);
setGPS_DynamicModel6();
#if defined(DEVMODE)
Serial.println(F("ublox DynamicModel6 enabled..."));
#endif
ublox_high_alt_mode_enabled = true;
}
while (!Serial1) {
delayMicroseconds(1); // wait for serial port to connect.
}
unsigned long start = millis();
unsigned long bekle = 0;
do
{
while (Serial1.available() > 0) {
char c;
c = Serial1.read();
gps.encode(c);
bekle = millis();
}
if (gps.time.isValid())
{
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(), NULL, NULL, NULL);
}
if (bekle != 0 && bekle + 10 < millis())break;
} while (millis() - start < ms);
}
float readBatt() {
float R1 = 560000.0; // 560K
float R2 = 100000.0; // 100K
float value = 0.0;
do {
value = analogRead(BattPin);
delay(5);// ilk açılışta hatalı ölçüm yapmaması için
value = analogRead(BattPin);
//value = value - 8; // direnç tolerans düzeltmesi
value = (value * 2.56) / 1024.0;
value = value / (R2 / (R1 + R2));
} while (value > 16.0);
return value ;
}
void Timer3Tick(void)
{
proceed = true;
}
void encode()
{
wdt_reset();
SiON;
delay(20);
si5351.init(SI5351_CRYSTAL_LOAD_0PF, 0, CORRECTION);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power if desired
si5351.output_enable(SI5351_CLK0, 0);
uint8_t i;
switch(cur_mode)
{
case MODE_JT9:
hf_freq = JT9_DEFAULT_FREQ;
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
tone_spacing = JT9_TONE_SPACING;
tone_delay = JT9_DELAY;
break;
case MODE_JT65:
hf_freq = JT65_DEFAULT_FREQ;
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
tone_spacing = JT65_TONE_SPACING;
tone_delay = JT65_DELAY;
break;
case MODE_JT4:
hf_freq = JT4_DEFAULT_FREQ;
symbol_count = JT4_SYMBOL_COUNT; // From the library defines
tone_spacing = JT4_TONE_SPACING;
tone_delay = JT4_DELAY;
break;
case MODE_WSPR:
hf_freq = WSPR_DEFAULT_FREQ;
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
tone_spacing = WSPR_TONE_SPACING;
tone_delay = WSPR_DELAY;
break;
case MODE_FT8:
hf_freq = FT8_DEFAULT_FREQ;
symbol_count = FT8_SYMBOL_COUNT; // From the library defines
tone_spacing = FT8_TONE_SPACING;
tone_delay = FT8_DELAY;
break;
case MODE_FSQ_2:
hf_freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_2_DELAY;
break;
case MODE_FSQ_3:
hf_freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_3_DELAY;
break;
case MODE_FSQ_4_5:
hf_freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_4_5_DELAY;
break;
case MODE_FSQ_6:
hf_freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_6_DELAY;
break;
}
set_tx_buffer();
// Now transmit the channel symbols
if(cur_mode == MODE_FSQ_2 || cur_mode == MODE_FSQ_3 || cur_mode == MODE_FSQ_4_5 || cur_mode == MODE_FSQ_6)
{
uint8_t j = 0;
while(tx_buffer[j++] != 0xff);
symbol_count = j - 1;
}
// Reset the tone to the base frequency and turn on the output
si5351.output_enable(SI5351_CLK0, 1);
uint32_t timer_period= tone_delay;
timer_period*=1000;
Timer3.initialize(timer_period);
Timer3.attachInterrupt(Timer3Tick);
Timer3.restart();
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((hf_freq * 100) + (tx_buffer[i] * tone_spacing), SI5351_CLK0);
proceed = false;
while (!proceed);
wdt_reset();
}
Timer3.stop();
// Turn off the output
si5351.output_enable(SI5351_CLK0, 0);
SiOFF;
}
void set_tx_buffer()
{
// Clear out the transmit buffer
memset(tx_buffer, 0, 255);
// Set the proper frequency and timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
jtencode.jt9_encode(hf_message, tx_buffer);
break;
case MODE_JT65:
jtencode.jt65_encode(hf_message, tx_buffer);
break;
case MODE_JT4:
jtencode.jt4_encode(hf_message, tx_buffer);
break;
case MODE_WSPR:
jtencode.wspr_encode(hf_call, hf_loc, dbm, tx_buffer);
break;
case MODE_FT8:
jtencode.ft8_encode(hf_message, tx_buffer);
break;
case MODE_FSQ_2:
case MODE_FSQ_3:
case MODE_FSQ_4_5:
case MODE_FSQ_6:
jtencode.fsq_dir_encode(hf_call, "n0call", ' ', "hello world", tx_buffer);
break;
}
}
void GridLocator(char *dst, float latt, float lon) {
int o1, o2;
int a1, a2;
float remainder;
// longitude
remainder = lon + 180.0;
o1 = (int)(remainder / 20.0);
remainder = remainder - (float)o1 * 20.0;
o2 = (int)(remainder / 2.0);
// latitude
remainder = latt + 90.0;
a1 = (int)(remainder / 10.0);
remainder = remainder - (float)a1 * 10.0;
a2 = (int)(remainder);
dst[0] = (char)o1 + 'A';
dst[1] = (char)a1 + 'A';
dst[2] = (char)o2 + '0';
dst[3] = (char)a2 + '0';
dst[4] = (char)0;
}
void freeMem() {
#if defined(DEVMODE)
Serial.print(F("Free RAM: ")); Serial.print(freeMemory()); Serial.println(F(" byte"));
#endif
}
void gpsDebug() {
#if defined(DEVMODE)
Serial.println();
Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Chars Sentences Checksum"));
Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- RX RX Fail"));
Serial.println(F("-----------------------------------------------------------------------------------------------------------------"));
printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
printInt(gps.location.age(), gps.location.isValid(), 5);
printDateTime(gps.date, gps.time);
printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);
printInt(gps.charsProcessed(), true, 6);
printInt(gps.sentencesWithFix(), true, 10);
printInt(gps.failedChecksum(), true, 9);
Serial.println();
#endif
}
static void printFloat(float val, bool valid, int len, int prec)
{
#if defined(DEVMODE)
if (!valid)
{
while (len-- > 1)
Serial.print('*');
Serial.print(' ');
}
else
{
Serial.print(val, prec);
int vi = abs((int)val);
int flen = prec + (val < 0.0 ? 2 : 1);
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
for (int i = flen; i < len; ++i)
Serial.print(' ');
}
#endif
}
static void printInt(unsigned long val, bool valid, int len)
{
#if defined(DEVMODE)
char sz[32] = "*****************";
if (valid)
sprintf(sz, "%ld", val);
sz[len] = 0;
for (int i = strlen(sz); i < len; ++i)
sz[i] = ' ';
if (len > 0)
sz[len - 1] = ' ';
Serial.print(sz);
#endif
}
static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
#if defined(DEVMODE)
if (!d.isValid())
{
Serial.print(F("********** "));
}
else
{
char sz[32];
sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
Serial.print(sz);
}
if (!t.isValid())
{
Serial.print(F("******** "));
}
else
{
char sz[32];
sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
Serial.print(sz);
}
printInt(d.age(), d.isValid(), 5);
#endif
}
static void printStr(const char *str, int len)
{
#if defined(DEVMODE)
int slen = strlen(str);
for (int i = 0; i < len; ++i)
Serial.print(i < slen ? str[i] : ' ');
#endif
}
//following GPS code from : https://github.com/HABduino/HABduino/blob/master/Software/habduino_v4/habduino_v4.ino
void setGPS_DynamicModel6()
{
int gps_set_sucess=0;
uint8_t setdm6[] = {
0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06,
0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00,
0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC };
while(!gps_set_sucess)
{
sendUBX(setdm6, sizeof(setdm6)/sizeof(uint8_t));
gps_set_sucess=getUBX_ACK(setdm6);
}
}
void sendUBX(uint8_t *MSG, uint8_t len) {
Serial1.flush();
Serial1.write(0xFF);
delay(500);
for(int i=0; i<len; i++) {
Serial1.write(MSG[i]);
}
}
boolean getUBX_ACK(uint8_t *MSG) {
uint8_t b;
uint8_t ackByteID = 0;
uint8_t ackPacket[10];
unsigned long startTime = millis();
// Construct the expected ACK packet
ackPacket[0] = 0xB5; // header
ackPacket[1] = 0x62; // header
ackPacket[2] = 0x05; // class
ackPacket[3] = 0x01; // id
ackPacket[4] = 0x02; // length
ackPacket[5] = 0x00;
ackPacket[6] = MSG[2]; // ACK class
ackPacket[7] = MSG[3]; // ACK id
ackPacket[8] = 0; // CK_A
ackPacket[9] = 0; // CK_B
// Calculate the checksums
for (uint8_t ubxi=2; ubxi<8; ubxi++) {
ackPacket[8] = ackPacket[8] + ackPacket[ubxi];
ackPacket[9] = ackPacket[9] + ackPacket[8];
}
while (1) {
// Test for success
if (ackByteID > 9) {
// All packets in order!
return true;
}
// Timeout if no valid response in 3 seconds
if (millis() - startTime > 3000) {
return false;
}
// Make sure data is available to read
if (Serial1.available()) {
b = Serial1.read();
// Check that bytes arrive in sequence as per expected ACK packet
if (b == ackPacket[ackByteID]) {
ackByteID++;
}
else {
ackByteID = 0; // Reset and look again, invalid order
}
}
}
}

150
README.md 100644 → 100755
Wyświetl plik

@ -1,2 +1,148 @@
# LightAPRS-W-1.0
Arduino based APRS &amp; WSPR Tracker
# LightAPRS-W Tracker
LightAPRS-W is an affordable, smallest, lightest, powerful and open source APRS tracker with WSPR. It makes tracking pico balloons simple and easy.
It is able to report location, altitude, temperature and pressure to the internet ([APRS-IS](http://status.aprs2.net)) or direct to an amateur radio once a minute with a solar panel/super capacitors or just 4xAAA batteries.
Because LightAPRS-W is open source you can add your own custom sensors via I2C/SPI pins.
LightAPRS-W will be soon available on http://www.qrp-labs.com for order.
<img src="images/lightaprs-w-pinout.jpg" width="600">
**Important :** LightAPRS-W uses the amateur 2 meter (VHF) and 10m+ (HF) radio band which requires an amateur radio license to operate.
## Basic Features
- **Software** : Open Source
- **Weight** : 9 grams
- **Dimensions** : 3.5 cm x 6 cm
- **IDE** : Arduino
- **Platform** : MightyCore
- **CPU** : Atmega1284P-AU
- **Flash** : 128 kB
- **Ram** : 16 kB
- **EEPROM** : 4 kB
- **Operating Frequency** : 8 Mhz
- **Operating Voltage** : 3.3 Volt
- **Input Voltage** : 4.5 (min) - 15 (max) Volt for 0.5 Watt / 4.5 (min) - 10 (max) Volt for 1 Watt via usb or VBat pin
- **BOD** : 2.7 Volt
- **Sensor** : BMP180 (pressure and temperature)
- **VHF Radio Module** : [Dorji DRA818V](http://www.dorji.com/products-detail.php?ProId=55) (included)
- **VHF Radio Operating Frequency** : 144-146 Mhz (configurable by code)
- **VHF Low Pass Filter** : Available (7 elements)
- **VHF Radio Power** : 0.5 Watt or 1 Watt (configurable by code)
- **VHF Power Consumption (TX)** : ~460 mA (0.5 Watt) / ~760 mA (1 Watt) (Automatically selected based on input voltage by code)
- **HF Radio Module** : [Si5351A-B-GT](https://www.silabs.com/products/timing/clocks/cmos-clock-generators/device.si5351a-b-gt) (included)
- **HF Radio Operating Frequency** : 2.5kHz - 200Mhz (configurable by code)
- **HF Low Pass Filter** : No
<img src="images/lightaprs-w-si5351_clck_out.png" width="500">
- **HF Radio Power** : ~10mW
- **Power Consumption (Sleep)** : ~5 mA
- **GPS** : Ublox MAX-M8Q (GPS-GLONASS)
- **GPS Antenna Gain** : 4.3 dBi
- **Extended Pins** : I2C, SPI
- **USB Serial** : CH340G
## Configuration
To programme LightAPRS-W Tracker, all you need is a micro usb (B type) cable, a few installations and configurations.
### 1.Install CH340G Driver
The CH340 chip is used by a number of Arduino compatible boards (and by LightAPRS-W) to provide USB connectivity, you may need to install a driver. (If you have installed it before for a clone Android board, you don't have to install it again.)
Don't worry, it's really easy. Just download the following driver from sparkfun.com and install it.
- [Windows](https://cdn.sparkfun.com/assets/learn_tutorials/8/4/4/CH341SER.EXE)
- [Mac](https://cdn.sparkfun.com/assets/learn_tutorials/8/4/4/CH341SER_MAC.ZIP)
- [Linux](https://cdn.sparkfun.com/assets/learn_tutorials/8/4/4/CH341SER_LINUX.ZIP)
<img src="images/ch340-driver-install.png" width="500">
### 2.Install Arduino IDE
Download and install [Arduino IDE](https://www.arduino.cc/en/Main/Software). If you have already installed Arduino, please check for updates. Its version should be at least v1.8.7 or newer.
### 3.Install MightyCore
- Open the Arduino IDE.
- Open the **File > Preferences** menu item. [[Screenshot]](images/arduino-preferences-boards-manager-url.png)
- Enter the following URL in Additional Boards Manager URLs:
> https://mcudude.github.io/MightyCore/package_MCUdude_MightyCore_index.json
- Separate the URLs using a comma ( , ) if you have more than one URL
- Open the **Tools > Board > Boards Manager...** menu item. [[Screenshot]](images/arduino-tools-boards-manager.png)
- Wait for the platform indexes to finish downloading.
- Type "MightyCore" in search bar until you see the **MightyCore** entry and click on it. <img src="images/arduino-boards-manager-mightycore-install.png">
- Click **Install** .
- After installation is complete, close the **Boards Manager** window.
### 4.Configure MightyCore
- Open the **Tools > Board** menu item and select **ATMega1284** from the end of the list. [[Screenshot]](images/arduino-boards-manager-mightycore-select.png)
- After selecting **ATMega1284** you will see new options under **Tools** menu. Select these options as follows:
- Clock: 8 MHz external
- BOD: 2.7v
- Pinout: Standart
- Variant: 1284P
- Compiler LTO : Disabled
<img src="images/lightaprs-mightycore-settings.png" width="300">
### 5.Copy Libraries & Compile Source Code
You are almost ready to programme LightAPRS-W Tracker :)
- First download the repository to your computer using green "clone or download" button.
- There are more then one Arduino projects optimized for different use cases. For example if you are planning to use LightAPRS-W tracker for a pico balloon project, then use "[LightAPRS-W-pico-balloon](LightAPRS-W-pico-balloon)" folder.
- You will notice some folders in the "libraries" folder. You have to copy these folders (libraries) into your Arduino libraries folder on your computer. Path to your Arduino libraries:
- **Windows** : This PC\Documents\Arduino\libraries\
- **Mac** : /Users/\<username\>/Documents/Arduino/libraries/ <img src="images/lightaprs-library-copy.png" width="600">
**IMPORTANT :** LightAPRS-W uses more libraries than LightAPRS. So if you purchased LightAPRS and copied libraries before, do it again for LightAPRS-W. Otherwise you get compile error.
- Then open the *.ino file with Arduino IDE and change your settings (Callsign, SSID, comment, etc.)
- Click **Verify**
### 6.Upload
- First attach an VHF antenna (at least 50cm monopole wire) to your tracker. Radio module may be damaged when not attaching an antenna, since power has nowhere to go.
- Connect LightAPRS-W Tracker to your computer with micro USB cable.
- If you have succesfully installed CH340G driver explained in the first step, you should see a COM port under **Tools->Port** menu item. Select that port.
<img src="images/lightaprs-arduino-port-select.png" width="600">
- Click **Upload**
- Your tracker is ready to launch :)
## Support
If you have any questions or need supoort, please contact support@lightaprs.com
## FAQ
**Q. I'm interested in pico balloon flights but I have no experience. What kind of balloon, solar panel, capacitor, etc. should I use?**
A. Please check out our wiki page Tips & Tricks for Pico Balloons: https://github.com/lightaprs/LightAPRS-1.0/wiki/Tips-&-Tricks-for-Pico-Balloons
**Q. What kind of antenna do i need to use on LightAPRS-W?**
A. You can use any type. For airborne projects (such as pico balloons) we suggest quarter wave monopole antenna for VHF (2m APRS) becuase this makes your payload lighter. So just cut a light 50 cm. wire and solder it to antenna footprint (A1) Since your payload is airborn and wavelenght is 2 meters, you don't need a wire for ground.
But for HF length of antenna depends on your band chose for WSPR. For example if it's 20 meters, you need 5m for TX RF output and 5m for ground. Tie one end to the balloon and solder the other end to the groundplane of the PCB. Then solder another 5m of wire to the TX RF output on the PCB, and let it hang below.
<img src="images/lightaprs-monopole-wire-antenna-connection.jpg" width="600">
**Q. But I want to use rubber duck antenna. Is it possible?**
A. Yes. But you need a pcb type SMA connector (male or female) as follows:
<img src="images/lightaprs-sma-antenna-connection.jpg" width="600">
**Q. I would like to use it as a car tracker is it possible to use an external antenna?**
A. Yes. But you also need a "n" type connector (n to SMA) along with the SMA connector as follows:
<img src="images/sma-to-type-n-connector.jpg" width="600">
We have tested it for VHF (2m APRS) with Midland NW-2000 and for HF (20m WSPR) with Diamond HF20FX worked fine :)
<img src="images/lightaprs-external-antenna-connection.jpg" width="600">

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 90 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 109 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 54 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 14 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 471 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 76 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 617 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 151 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 48 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 555 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 571 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 226 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 17 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 861 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 638 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 650 KiB

Wyświetl plik

@ -0,0 +1,297 @@
/***************************************************
This is a library for the Adafruit BMP085/BMP180 Barometric Pressure + Temp sensor
Designed specifically to work with the Adafruit BMP085 or BMP180 Breakout
----> http://www.adafruit.com/products/391
----> http://www.adafruit.com/products/1603
These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_BMP085.h"
Adafruit_BMP085::Adafruit_BMP085() {
}
boolean Adafruit_BMP085::begin(uint8_t mode) {
if (mode > BMP085_ULTRAHIGHRES)
mode = BMP085_ULTRAHIGHRES;
oversampling = mode;
Wire.begin();
if (read8(0xD0) != 0x55) return false;
/* read calibration data */
ac1 = read16(BMP085_CAL_AC1);
ac2 = read16(BMP085_CAL_AC2);
ac3 = read16(BMP085_CAL_AC3);
ac4 = read16(BMP085_CAL_AC4);
ac5 = read16(BMP085_CAL_AC5);
ac6 = read16(BMP085_CAL_AC6);
b1 = read16(BMP085_CAL_B1);
b2 = read16(BMP085_CAL_B2);
mb = read16(BMP085_CAL_MB);
mc = read16(BMP085_CAL_MC);
md = read16(BMP085_CAL_MD);
#if (BMP085_DEBUG == 1)
Serial.print("ac1 = "); Serial.println(ac1, DEC);
Serial.print("ac2 = "); Serial.println(ac2, DEC);
Serial.print("ac3 = "); Serial.println(ac3, DEC);
Serial.print("ac4 = "); Serial.println(ac4, DEC);
Serial.print("ac5 = "); Serial.println(ac5, DEC);
Serial.print("ac6 = "); Serial.println(ac6, DEC);
Serial.print("b1 = "); Serial.println(b1, DEC);
Serial.print("b2 = "); Serial.println(b2, DEC);
Serial.print("mb = "); Serial.println(mb, DEC);
Serial.print("mc = "); Serial.println(mc, DEC);
Serial.print("md = "); Serial.println(md, DEC);
#endif
return true;
}
int32_t Adafruit_BMP085::computeB5(int32_t UT) {
int32_t X1 = (UT - (int32_t)ac6) * ((int32_t)ac5) >> 15;
int32_t X2 = ((int32_t)mc << 11) / (X1+(int32_t)md);
return X1 + X2;
}
uint16_t Adafruit_BMP085::readRawTemperature(void) {
write8(BMP085_CONTROL, BMP085_READTEMPCMD);
delay(5);
#if BMP085_DEBUG == 1
Serial.print("Raw temp: "); Serial.println(read16(BMP085_TEMPDATA));
#endif
return read16(BMP085_TEMPDATA);
}
uint32_t Adafruit_BMP085::readRawPressure(void) {
uint32_t raw;
write8(BMP085_CONTROL, BMP085_READPRESSURECMD + (oversampling << 6));
if (oversampling == BMP085_ULTRALOWPOWER)
delay(5);
else if (oversampling == BMP085_STANDARD)
delay(8);
else if (oversampling == BMP085_HIGHRES)
delay(14);
else
delay(26);
raw = read16(BMP085_PRESSUREDATA);
raw <<= 8;
raw |= read8(BMP085_PRESSUREDATA+2);
raw >>= (8 - oversampling);
/* this pull broke stuff, look at it later?
if (oversampling==0) {
raw <<= 8;
raw |= read8(BMP085_PRESSUREDATA+2);
raw >>= (8 - oversampling);
}
*/
#if BMP085_DEBUG == 1
Serial.print("Raw pressure: "); Serial.println(raw);
#endif
return raw;
}
int32_t Adafruit_BMP085::readPressure(void) {
int32_t UT, UP, B3, B5, B6, X1, X2, X3, p;
uint32_t B4, B7;
UT = readRawTemperature();
UP = readRawPressure();
#if BMP085_DEBUG == 1
// use datasheet numbers!
UT = 27898;
UP = 23843;
ac6 = 23153;
ac5 = 32757;
mc = -8711;
md = 2868;
b1 = 6190;
b2 = 4;
ac3 = -14383;
ac2 = -72;
ac1 = 408;
ac4 = 32741;
oversampling = 0;
#endif
B5 = computeB5(UT);
#if BMP085_DEBUG == 1
Serial.print("X1 = "); Serial.println(X1);
Serial.print("X2 = "); Serial.println(X2);
Serial.print("B5 = "); Serial.println(B5);
#endif
// do pressure calcs
B6 = B5 - 4000;
X1 = ((int32_t)b2 * ( (B6 * B6)>>12 )) >> 11;
X2 = ((int32_t)ac2 * B6) >> 11;
X3 = X1 + X2;
B3 = ((((int32_t)ac1*4 + X3) << oversampling) + 2) / 4;
#if BMP085_DEBUG == 1
Serial.print("B6 = "); Serial.println(B6);
Serial.print("X1 = "); Serial.println(X1);
Serial.print("X2 = "); Serial.println(X2);
Serial.print("B3 = "); Serial.println(B3);
#endif
X1 = ((int32_t)ac3 * B6) >> 13;
X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16;
X3 = ((X1 + X2) + 2) >> 2;
B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15;
B7 = ((uint32_t)UP - B3) * (uint32_t)( 50000UL >> oversampling );
#if BMP085_DEBUG == 1
Serial.print("X1 = "); Serial.println(X1);
Serial.print("X2 = "); Serial.println(X2);
Serial.print("B4 = "); Serial.println(B4);
Serial.print("B7 = "); Serial.println(B7);
#endif
if (B7 < 0x80000000) {
p = (B7 * 2) / B4;
} else {
p = (B7 / B4) * 2;
}
X1 = (p >> 8) * (p >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7357 * p) >> 16;
#if BMP085_DEBUG == 1
Serial.print("p = "); Serial.println(p);
Serial.print("X1 = "); Serial.println(X1);
Serial.print("X2 = "); Serial.println(X2);
#endif
p = p + ((X1 + X2 + (int32_t)3791)>>4);
#if BMP085_DEBUG == 1
Serial.print("p = "); Serial.println(p);
#endif
return p;
}
int32_t Adafruit_BMP085::readSealevelPressure(float altitude_meters) {
float pressure = readPressure();
return (int32_t)(pressure / pow(1.0-altitude_meters/44330, 5.255));
}
float Adafruit_BMP085::readTemperature(void) {
int32_t UT, B5; // following ds convention
float temp;
UT = readRawTemperature();
#if BMP085_DEBUG == 1
// use datasheet numbers!
UT = 27898;
ac6 = 23153;
ac5 = 32757;
mc = -8711;
md = 2868;
#endif
B5 = computeB5(UT);
temp = (B5+8) >> 4;
temp /= 10;
return temp;
}
float Adafruit_BMP085::readAltitude(float sealevelPressure) {
float altitude;
float pressure = readPressure();
altitude = 44330 * (1.0 - pow(pressure /sealevelPressure,0.1903));
return altitude;
}
/*********************************************************************/
uint8_t Adafruit_BMP085::read8(uint8_t a) {
uint8_t ret;
Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device
#if (ARDUINO >= 100)
Wire.write(a); // sends register address to read from
#else
Wire.send(a); // sends register address to read from
#endif
Wire.endTransmission(); // end transmission
Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device
Wire.requestFrom(BMP085_I2CADDR, 1);// send data n-bytes read
#if (ARDUINO >= 100)
ret = Wire.read(); // receive DATA
#else
ret = Wire.receive(); // receive DATA
#endif
Wire.endTransmission(); // end transmission
return ret;
}
uint16_t Adafruit_BMP085::read16(uint8_t a) {
uint16_t ret;
Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device
#if (ARDUINO >= 100)
Wire.write(a); // sends register address to read from
#else
Wire.send(a); // sends register address to read from
#endif
Wire.endTransmission(); // end transmission
Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device
Wire.requestFrom(BMP085_I2CADDR, 2);// send data n-bytes read
#if (ARDUINO >= 100)
ret = Wire.read(); // receive DATA
ret <<= 8;
ret |= Wire.read(); // receive DATA
#else
ret = Wire.receive(); // receive DATA
ret <<= 8;
ret |= Wire.receive(); // receive DATA
#endif
Wire.endTransmission(); // end transmission
return ret;
}
void Adafruit_BMP085::write8(uint8_t a, uint8_t d) {
Wire.beginTransmission(BMP085_I2CADDR); // start transmission to device
#if (ARDUINO >= 100)
Wire.write(a); // sends register address to read from
Wire.write(d); // write data
#else
Wire.send(a); // sends register address to read from
Wire.send(d); // write data
#endif
Wire.endTransmission(); // end transmission
}

Wyświetl plik

@ -0,0 +1,79 @@
/***************************************************
This is a library for the Adafruit BMP085/BMP180 Barometric Pressure + Temp sensor
Designed specifically to work with the Adafruit BMP085 or BMP180 Breakout
----> http://www.adafruit.com/products/391
----> http://www.adafruit.com/products/1603
These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#ifndef ADAFRUIT_BMP085_H
#define ADAFRUIT_BMP085_H
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Wire.h"
#define BMP085_DEBUG 0
#define BMP085_I2CADDR 0x77
#define BMP085_ULTRALOWPOWER 0
#define BMP085_STANDARD 1
#define BMP085_HIGHRES 2
#define BMP085_ULTRAHIGHRES 3
#define BMP085_CAL_AC1 0xAA // R Calibration data (16 bits)
#define BMP085_CAL_AC2 0xAC // R Calibration data (16 bits)
#define BMP085_CAL_AC3 0xAE // R Calibration data (16 bits)
#define BMP085_CAL_AC4 0xB0 // R Calibration data (16 bits)
#define BMP085_CAL_AC5 0xB2 // R Calibration data (16 bits)
#define BMP085_CAL_AC6 0xB4 // R Calibration data (16 bits)
#define BMP085_CAL_B1 0xB6 // R Calibration data (16 bits)
#define BMP085_CAL_B2 0xB8 // R Calibration data (16 bits)
#define BMP085_CAL_MB 0xBA // R Calibration data (16 bits)
#define BMP085_CAL_MC 0xBC // R Calibration data (16 bits)
#define BMP085_CAL_MD 0xBE // R Calibration data (16 bits)
#define BMP085_CONTROL 0xF4
#define BMP085_TEMPDATA 0xF6
#define BMP085_PRESSUREDATA 0xF6
#define BMP085_READTEMPCMD 0x2E
#define BMP085_READPRESSURECMD 0x34
class Adafruit_BMP085 {
public:
Adafruit_BMP085();
boolean begin(uint8_t mode = BMP085_ULTRAHIGHRES); // by default go highres
float readTemperature(void);
int32_t readPressure(void);
int32_t readSealevelPressure(float altitude_meters = 0);
float readAltitude(float sealevelPressure = 101325); // std atmosphere
uint16_t readRawTemperature(void);
uint32_t readRawPressure(void);
private:
int32_t computeB5(int32_t UT);
uint8_t read8(uint8_t addr);
uint16_t read16(uint8_t addr);
void write8(uint8_t addr, uint8_t data);
uint8_t oversampling;
int16_t ac1, ac2, ac3, b1, b2, mb, mc, md;
uint16_t ac4, ac5, ac6;
};
#endif // ADAFRUIT_BMP085_H

Wyświetl plik

@ -0,0 +1,28 @@
This is a library for the Adafruit BMP085/BMP180 Barometric Pressure + Temp sensor
Designed specifically to work with the Adafruit BMP085 or BMP180 Breakout
----> http://www.adafruit.com/products/391
----> http://www.adafruit.com/products/1603
These displays use I2C to communicate, 2 pins are required to interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Check out the links above for our tutorials and wiring diagrams
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
To download. click the DOWNLOAD ZIP button, rename the uncompressed folder Adafruit_BMP085.
Check that the Adafruit_BMP085 folder contains Adafruit_BMP085.cpp and Adafruit_BMP085.h
Place the Adafruit_BMP085 library folder your arduinosketchfolder/libraries/ folder.
You may need to create the libraries subfolder if its your first library. Restart the IDE.
We also have a great tutorial on Arduino library installation at:
http://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use

Wyświetl plik

@ -0,0 +1,66 @@
#include <Wire.h>
#include <Adafruit_BMP085.h>
/***************************************************
This is an example for the BMP085 Barometric Pressure & Temp Sensor
Designed specifically to work with the Adafruit BMP085 Breakout
----> https://www.adafruit.com/products/391
These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here
Adafruit_BMP085 bmp;
void setup() {
Serial.begin(9600);
if (!bmp.begin()) {
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
while (1) {}
}
}
void loop() {
Serial.print("Temperature = ");
Serial.print(bmp.readTemperature());
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bmp.readPressure());
Serial.println(" Pa");
// Calculate altitude assuming 'standard' barometric
// pressure of 1013.25 millibar = 101325 Pascal
Serial.print("Altitude = ");
Serial.print(bmp.readAltitude());
Serial.println(" meters");
Serial.print("Pressure at sealevel (calculated) = ");
Serial.print(bmp.readSealevelPressure());
Serial.println(" Pa");
// you can get a more precise measurement of altitude
// if you know the current sea level pressure which will
// vary with weather and such. If it is 1015 millibars
// that is equal to 101500 Pascals.
Serial.print("Real altitude = ");
Serial.print(bmp.readAltitude(101500));
Serial.println(" meters");
Serial.println();
delay(500);
}

Wyświetl plik

@ -0,0 +1,9 @@
name=Adafruit BMP085 Library
version=1.0.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=A powerful but easy to use BMP085/BMP180 Library
paragraph=A powerful but easy to use BMP085/BMP180 Library
category=Sensors
url=https://github.com/adafruit/Adafruit-BMP085-Library
architectures=*

Wyświetl plik

@ -0,0 +1,186 @@
/*
* GEOFENCE.c
*
* Created: 5.11.2016 22:04:58
* Author: Tomy2
*/
#include "GEOFENCE.h"
/*
Adapted version of pointInPolygon() function from: http://alienryderflex.com/polygon/
Returns '0' if the point is outside of the polygon and '1' if it's inside.
Expects input DEGREES * 100000 for latitude and longitude. Eg 4961070 for 49.61070 N.
The reason is to make sure all calculations fit inside int32_t.
However, this function is not very accurate due to rounding within the computation.
*/
int32_t pointInPolygon(int32_t polyCorners, int32_t * polygon, int32_t latitude, int32_t longitude)
{
int32_t i;
int32_t j = polyCorners * 2 - 2;
int32_t oddNodes = 0;
for(i = 0; i < polyCorners * 2; i += 2)
{
if((polygon[i + 1] < latitude && polygon[j + 1] >= latitude
|| polygon[j + 1] < latitude && polygon[i + 1] >= latitude)
&& (polygon[i] <= longitude || polygon[j] <= longitude))
{
oddNodes ^= (polygon[i] + (latitude - polygon[i + 1])
/ (polygon[j + 1] - polygon[i + 1]) * (polygon[j] - polygon[i]) < longitude);
}
j = i;
}
return oddNodes;
}
/*
Adapted version of pointInPolygon() function from: http://alienryderflex.com/polygon/
Returns '0' if the point is outside of the polygon and '1' if it's inside.
Uses FLOAT input for better accuracy.
*/
int32_t pointInPolygonF(int32_t polyCorners, float * polygon, float latitude, float longitude)
{
int32_t i;
int32_t j = polyCorners * 2 - 2;
int32_t oddNodes = 0;
for(i = 0; i < polyCorners * 2; i += 2)
{
if((polygon[i + 1] < latitude && polygon[j + 1] >= latitude
|| polygon[j + 1] < latitude && polygon[i + 1] >= latitude)
&& (polygon[i] <= longitude || polygon[j] <= longitude))
{
oddNodes ^= (polygon[i] + (latitude - polygon[i + 1])
/ (polygon[j + 1] - polygon[i + 1]) * (polygon[j] - polygon[i]) < longitude);
}
j = i;
}
return oddNodes;
}
/*
Changes GEOFENCE_APRS_frequency and GEOFENCE_no_tx global variables based on the input coordinates.
FREQUENCIES:
Africa 144.800
Europe 144.800
Russia 144.800
Canada 144.390
Mexico 144.390
USA 144.390
Costa Rica 145.010
Nicaragua 145.010
Panama 145.010
Venezuela 145.010
Brazil 145.570
Colombia 144.390
Chile 144.390
Argentina 144.930
Paraguay 144.930
Uruguay 144.930
China 144.640
Japan 144.660
South Korea 144.620
Thailand 145.525
Australia 145.175
New Zealand 144.575
Indonesia 144.390
Malaysia 144.390
NO AIRBORNE APRS:
France
Latvia
United Kingdom
Expected input FLOAT for latitude and longitude as in GPS_UBX_latitude_Float and GPS_UBX_longitude_Float.
*/
void GEOFENCE_position(float latitude, float longitude)
{
// SECTOR 1
if(longitude > -38.0 && longitude < 73.0)
{
// S 1/2
if(latitude > 0.0)
{
if(pointInPolygonF(9, UKF, latitude, longitude) == 1) {GEOFENCE_no_tx = 1;GEOFENCE_APRS_frequency = 144800000;}
else if(pointInPolygonF(10, LatviaF, latitude, longitude) == 1) {GEOFENCE_no_tx = 1;GEOFENCE_APRS_frequency = 144800000;}
else {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144800000;}
}
// S 2/2
else
{
if(pointInPolygonF(9, BrazilF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145570000;}
else {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144800000;}
}
}
// SECTOR 2
else if(longitude <= -38.0)
{
// S 1/2
if(latitude > 12.5)
{
{GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144390000;}
}
// S 2/2
else
{
if(pointInPolygonF(8, ArgParUruF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144930000;}
else if(pointInPolygonF(9, BrazilF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145570000;}
else if(pointInPolygonF(7, VenezuelaF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145010000;}
else if(pointInPolygonF(5, CostNicPanF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145010000;}
else {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144390000;}
}
}
// SECTOR 3
else if(longitude >= 73.0)
{
// S 1/2
if(latitude > 19.2)
{
if(pointInPolygonF(12, ChinaF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144640000;}
else if(pointInPolygonF(7, JapanF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144660000;}
else if(pointInPolygonF(5, South_KoreaF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144620000;}
else if(pointInPolygonF(5, ThailandF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145525000;}
else {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144800000;}
}
// S 2/2
else
{
if(pointInPolygonF(6, AustraliaF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145175000;}
else if(pointInPolygonF(5, New_ZealandF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144575000;}
else if(pointInPolygonF(5, ThailandF, latitude, longitude) == 1) {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 145525000;}
else {GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144390000;}
}
}
// shouldn't get here
else
{
{GEOFENCE_no_tx = 0; GEOFENCE_APRS_frequency = 144800000;}
}
}

Wyświetl plik

@ -0,0 +1,169 @@
#ifndef GEOFENCE_H
#define GEOFENCE_H
#include "stdint.h"
// GEOFENCE ARRAYS (longitude, latitude)
static float ArgParUruF[] = {
-57.79910, -18.67750,
-53.48140, -26.66710,
-57.13990, -29.77390,
-49.62520, -34.51560,
-60.24900, -58.56250,
-73.49850, -50.35950,
-67.54390, -21.43260,
-57.79910, -18.67750
};
static float AustraliaF[] = {
147.56840, -46.92030,
166.02540, -29.15220,
144.14060, -9.18890,
98.78910, -11.69530,
112.41210, -39.77480,
147.56840, -46.92030
};
static float BrazilF[] = {
-57.04100, -29.76440,
-49.65820, -34.45220,
-28.30080, -5.87830,
-51.50390, 4.30260,
-60.55660, 5.00340,
-74.17970, -6.49000,
-57.74410, -18.56290,
-53.34960, -26.66710,
-57.04100, -29.76440
};
static float ChinaF[] = {
87.18750, 49.38240,
77.51104, 44.59703,
71.63090, 36.94990,
93.03226, 25.16517,
110.39060, 15.96130,
124.76074, 18.47961,
124.54103, 36.73889,
132.17135, 41.06670,
136.35137, 47.03273,
123.44237, 54.41894,
104.58986, 45.05798,
87.18750, 49.38240
};
static float CostNicPanF[] = {
-88.76950, 11.99630,
-80.20020, 4.80640,
-76.61870, 9.42740,
-82.70510, 15.39010,
-88.76950, 11.99630
};
static float FranceF[] = {
7.95410, 48.76340,
1.91160, 50.94460,
-3.64750, 48.29780,
-1.49410, 43.34120,
2.98830, 42.56930,
7.40480, 43.78700,
5.88870, 46.45300,
7.95410, 48.76340
};
static float JapanF[] = {
138.71337, 49.79545,
130.96153, 38.83591,
124.66191, 34.18805,
124.76075, 24.68693,
144.22850, 23.40280,
156.09372, 49.72447,
138.71337, 49.79545
};
static float LatviaF[] = {
26.64180, 55.68380,
28.17990, 56.20670,
27.78440, 57.33250,
25.00490, 58.00230,
24.14790, 57.17200,
21.78590, 57.68650,
20.81910, 56.07200,
22.19240, 56.44430,
25.68600, 56.18230,
26.64180, 55.68380
};
static float New_ZealandF[] = {
179.99990, -37.78810,
179.99990, -55.22580,
154.33590, -45.82880,
172.26560, -28.76770,
179.99990, -37.78810
};
static float RomaniaF[] = {
27.12520, 47.95310,
22.95040, 47.97520,
20.63230, 45.85940,
23.24710, 43.86620,
29.13570, 43.85040,
27.12520, 47.95310
};
static float South_KoreaF[] = {
132.17653, 41.01310,
124.61794, 36.70367,
124.65089, 34.26179,
130.87903, 38.87847,
132.17653, 41.01310
};
static float ThailandF[] = {
110.23679, 15.96138,
93.60347, 24.72692,
94.89994, 6.03131,
104.89745, 6.07500,
110.23679, 15.96138
};
static float UKF[] = {
-0.65920, 60.97310,
-7.58060, 58.07790,
-8.21780, 54.23960,
-4.76810, 53.80070,
-5.86670, 49.76710,
1.30740, 50.85450,
1.86770, 52.78950,
-2.04350, 55.97380,
-0.65920, 60.97310
};
static float VenezuelaF[] = {
-66.65410, 0.18680,
-60.99610, 5.41910,
-59.52390, 9.38400,
-72.15820, 12.49020,
-72.48780, 7.10090,
-67.96140, 5.72530,
-66.65410, 0.18680
};
// VARIABLES
extern uint32_t GEOFENCE_APRS_frequency;
extern uint32_t GEOFENCE_no_tx;
// FUNCTIONS
int32_t pointInPolygon(int32_t polyCorners, int32_t * polygon, int32_t latitude, int32_t longitude);
int32_t pointInPolygonF(int32_t polyCorners, float * polygon, float latitude, float longitude);
void GEOFENCE_position(float latitude, float longitude);
#endif

Wyświetl plik

@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS

Wyświetl plik

@ -0,0 +1,302 @@
JT65/JT9/JT4/FT8/WSPR/FSQ Encoder Library for Arduino
=====================================================
This library very simply generates a set of channel symbols for JT65, JT9, JT4, FT8, or WSPR based on the user providing a properly formatted Type 6 message for JT65, JT9, or JT4 (which is 13 valid characters), Type 0.0 or 0.5 message for FT8 (v2.0.0 protocol) or a callsign, Maidenhead grid locator, and power output for WSPR. It will also generate an arbitrary FSQ message of up to 200 characters in both directed and non-directed format. When paired with a synthesizer that can output frequencies in fine, phase-continuous tuning steps (such as the Si5351), then a beacon or telemetry transmitter can be created which can change the transmitted characters as needed from the Arduino.
Please feel free to use the issues feature of GitHub if you run into problems or have suggestions for important features to implement.
Thanks For Your Support!
------------------------
If you would like to support my library development efforts, I would ask that you please consider sending a [PayPal tip](https://paypal.me/NT7S). Thank you!
Hardware Requirements and Setup
-------------------------------
This library has been written for the Arduino platform and has been successfully tested on the Arduino Uno, an Uno clone, an Arduino Zero clone, and a NodeMCU. Since the library itself does not access the hardware, there is no reason it should not run on any Arduino model of recent vintage as long as it has at least 2 kB of RAM.
How To Install
--------------
The best way to install the library is via the Arduino Library Manager, which is available if you are using Arduino IDE version 1.6.2 or greater. To install it this way, simply go to the menu Sketch > Include Library > Manage Libraries..., and then in the search box at the upper-right, type "Etherkit JTEncode". Click on the entry in the list below, then click on the provided "Install" button. By installing the library this way, you will always have notifications of future library updates, and can easily switch between library versions.
If you need to or would like to install the library in the old way, then you can download a copy of the library in a ZIP file. Download a ZIP file of the library from the GitHub repository by going to [this page](https://github.com/etherkit/JTEncode/releases) and clicking the "Source code (zip)" link under the latest release. Finally, open the Arduino IDE, select menu Sketch > Import Library... > Add Library..., and select the ZIP that you just downloaded.
RAM Usage
---------
Most of the encoding functions need to manipulate multiple arrays of symbols in RAM at the same time, and therefore are quite RAM intensive. Care has been taken to put as much data into program memory as is possible, but the encoding functions still can cause problems with the low RAM microcontrollers such as the ATmegaxx8 series. If you are using these, then please be sure to call them only once when a transmit buffer needs to be created or changed, and call them separately of other subroutine calls. When using other microcontrollers that have more RAM, such as most of the ARM ICs, this won't be as much of a problem. If you see unusual freezes, that almost certainly indicates a RAM shortage.
Example
-------
There is a simple example that is placed in your examples menu under JTEncode. Open this to see how to incorporate this library with your code. The example provided with with the library is meant to be used in conjunction with the [Etherkit Si5351A Breakout Board](https://www.etherkit.com/rf-modules/si5351a-breakout-board.html), although it could be modified to use with other synthesizers which meet the technical requirements of the JT65/JT9/JT4/WSPR/FSQ modes.
To run this example, be sure to download the [Si5351Arduino](https://github.com/etherkit/Si5351Arduino) library and follow the instructions there to connect the Si5351A Breakout Board to your Arduino. In order to trigger transmissions, you will also need to connect a momentary pushbutton from pin 12 of the Arduino to ground.
The example sketch itself is fairly straightforward. JT65, JT9, JT4, FT8, WSPR, and FSQ modes are modulated in same way: phase-continuous multiple-frequency shift keying (MFSK). The message to be transmitted is passed to the JTEncode method corresponding to the desired mode, along with a pointer to an array which holds the returned channel symbols. When the pushbutton is pushed, the sketch then transmits each channel symbol sequentially as an offset from the base frequency given in the sketch define section.
An instance of the JTEncode object is created:
JTEncode jtencode;
On sketch startup, the mode parameters are set based on which mode is currently selected (by the DEFAULT_MODE define):
// Set the proper frequency, tone spacing, symbol count, and
// tone delay depending on mode
switch(cur_mode)
{
case MODE_JT9:
freq = JT9_DEFAULT_FREQ;
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
tone_spacing = JT9_TONE_SPACING;
tone_delay = JT9_DELAY;
break;
case MODE_JT65:
freq = JT65_DEFAULT_FREQ;
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
tone_spacing = JT65_TONE_SPACING;
tone_delay = JT65_DELAY;
break;
case MODE_JT4:
freq = JT4_DEFAULT_FREQ;
symbol_count = JT4_SYMBOL_COUNT; // From the library defines
tone_spacing = JT4_TONE_SPACING;
tone_delay = JT4_DELAY;
break;
case MODE_WSPR:
freq = WSPR_DEFAULT_FREQ;
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
tone_spacing = WSPR_TONE_SPACING;
tone_delay = WSPR_DELAY;
break;
case MODE_FT8:
freq = FT8_DEFAULT_FREQ;
symbol_count = FT8_SYMBOL_COUNT; // From the library defines
tone_spacing = FT8_TONE_SPACING;
tone_delay = FT8_DELAY;
break;
case MODE_FSQ_2:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_2_DELAY;
break;
case MODE_FSQ_3:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_3_DELAY;
break;
case MODE_FSQ_4_5:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_4_5_DELAY;
break;
case MODE_FSQ_6:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_6_DELAY;
break;
}
Note that the number of channel symbols for each mode is defined in the library, so you can use those defines to initialize your own symbol array sizes.
Before transmit, the proper class method is chosen based on the desired mode, then the transmit symbol buffer and the other mode information is set:
// Set the proper frequency and timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
jtencode.jt9_encode(message, tx_buffer);
break;
case MODE_JT65:
jtencode.jt65_encode(message, tx_buffer);
break;
case MODE_JT4:
jtencode.jt4_encode(message, tx_buffer);
break;
case MODE_WSPR:
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
break;
case MODE_FT8:
jtencode.ft_encode(message, tx_buffer);
break;
case MODE_FSQ_2:
case MODE_FSQ_3:
case MODE_FSQ_4_5:
case MODE_FSQ_6:
jtencode.fsq_dir_encode(call, "n0call", " ", "hello world", tx_buffer);
break;
}
As mentioned above, it is best if the message encoding functions are called only when needed, in its own subroutine.
Once the channel symbols have been generated, it is a simple matter of transmitting them in sequence, each the correct amount of time:
// Now transmit the channel symbols
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((freq * 100) + (tx_buffer[i] * tone_spacing), SI5351_CLK0);
delay(tone_delay);
}
Public Methods
------------------
### jt65_encode()
```
/*
* jt65_encode(const char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least size JT65_SYMBOL_COUNT to the method.
*
*/
```
### jt9_encode()
```
/*
* jt9_encode(const char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least size JT9_SYMBOL_COUNT to the method.
*
*/
```
### jt4_encode()
```
/*
* jt4_encode(const char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least size JT9_SYMBOL_COUNT to the method.
*
*/
```
### wspr_encode()
```
/*
* wspr_encode(const char * call, const char * loc, const uint8_t dbm, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
*
* call - Callsign (6 characters maximum).
* loc - Maidenhead grid locator (4 characters maximum).
* dbm - Output power in dBm.
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least size WSPR_SYMBOL_COUNT to the method.
*
*/
```
### ft8_encode()
```
/*
* ft8_encode(const char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters or a telemetry message
* of up to 18 hexadecimal digit (in string format) and returns a channel symbol table.
* Encoded for the FT8 protocol used in WSJT-X v2.0 and beyond (79 channel symbols).
*
* message - Type 0.0 free text message or Type 0.5 telemetry message.
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least size FT8_SYMBOL_COUNT to the method.
*
*/
```
### fsq_encode()
```
/*
* fsq_encode(const char * from_call, const char * message, uint8_t * symbols)
*
* Takes an arbitrary message and returns a FSQ channel symbol table.
*
* from_call - Callsign of issuing station (maximum size: 20)
* message - Null-terminated message string, no greater than 130 chars in length
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least the size of the message
* plus 5 characters to the method. Terminated in 0xFF.
*
*/
```
### fsq_dir_encode()
```
/*
* fsq_dir_encode(const char * from_call, const char * to_call, const char cmd, const char * message, uint8_t * symbols)
*
* Takes an arbitrary message and returns a FSQ channel symbol table.
*
* from_call - Callsign from which message is directed (maximum size: 20)
* to_call - Callsign to which message is directed (maximum size: 20)
* cmd - Directed command
* message - Null-terminated message string, no greater than 100 chars in length
* symbols - Array of channel symbols to transmit returned by the method.
* Ensure that you pass a uint8_t array of at least the size of the message
* plus 5 characters to the method. Terminated in 0xFF.
*
*/
```
Tokens
------
Here are the defines, structs, and enumerations you will find handy to use with the library.
Defines:
JT65_SYMBOL_COUNT, JT9_SYMBOL_COUNT, JT4_SYMBOL_COUNT, WSPR_SYMBOL_COUNT, FT8_SYMBOL_COUNT
Acknowledgements
----------------
Many thanks to Joe Taylor K1JT for his innovative work in amateur radio. We are lucky to have him. The algorithms in this program were derived from the source code in the [WSJT-X](https://sourceforge.net/p/wsjt/) suite of applications. Also, many thanks for Andy Talbot G4JNT for [his paper](http://www.g4jnt.com/JTModesBcns.htm) on the WSPR coding protocol, which helped me to understand the WSPR encoding process, which in turn helped me to understand the related JT protocols.
Also, a big thank you to Murray Greenman, ZL1BPU for working allowing me to pick his brain regarding his neat new mode FSQ.
Changelog
---------
* v1.2.0
* Add support for FT8 protocol (79 symbol version introduced December 2018)
* v1.1.3
* Add support for ESP8266
* Fix WSPR regression in last release
* v1.1.2
* Fix buffer bug in _jt_message_prep()_ that caused messages of 11 chars to lock up the processor
* Made a handful of changes to make the library more friendly to ATmegaxx8 processors
* Rewrote example sketch to be generically compatible with most Arduino platforms
* v1.1.1
* Update example sketch for Si5351Arduino v2.0.0
* v1.1.0
* Added FSQ
* v1.0.1
* Fixed a bug in _jt65_interleave()_ that was causing a buffer overrun.
* v1.0.0
* Initial Release
License
-------
JTEncode 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.
JTEncode 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 JTEncode. If not, see <http://www.gnu.org/licenses/>.

Wyświetl plik

@ -0,0 +1,251 @@
//
// Simple JT65/JT9/JT4/FT8/WSPR/FSQ beacon for Arduino, with the Etherkit
// Si5351A Breakout Board, by Jason Milldrum NT7S.
//
// Transmit an abritrary message of up to 13 valid characters
// (a Type 6 message) in JT65, JT9, JT4, a type 0.0 or type 0.5 FT8 message,
// a FSQ message, or a standard Type 1 message in WSPR.
//
// Connect a momentary push button to pin 12 to use as the
// transmit trigger. Get fancy by adding your own code to trigger
// off of the time from a GPS or your PC via virtual serial.
//
// Original code based on Feld Hell beacon for Arduino by Mark
// Vandewettering K6HX, adapted for the Si5351A by Robert
// Liesenfeld AK6L <ak6l@ak6l.org>.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#include <si5351.h>
#include <JTEncode.h>
#include <rs_common.h>
#include <int.h>
#include <string.h>
#include "Wire.h"
// Mode defines
#define JT9_TONE_SPACING 174 // ~1.74 Hz
#define JT65_TONE_SPACING 269 // ~2.69 Hz
#define JT4_TONE_SPACING 437 // ~4.37 Hz
#define WSPR_TONE_SPACING 146 // ~1.46 Hz
#define FSQ_TONE_SPACING 879 // ~8.79 Hz
#define FT8_TONE_SPACING 625 // ~6.25 Hz
#define JT9_DELAY 576 // Delay value for JT9-1
#define JT65_DELAY 371 // Delay in ms for JT65A
#define JT4_DELAY 229 // Delay value for JT4A
#define WSPR_DELAY 683 // Delay value for WSPR
#define FSQ_2_DELAY 500 // Delay value for 2 baud FSQ
#define FSQ_3_DELAY 333 // Delay value for 3 baud FSQ
#define FSQ_4_5_DELAY 222 // Delay value for 4.5 baud FSQ
#define FSQ_6_DELAY 167 // Delay value for 6 baud FSQ
#define FT8_DELAY 159 // Delay value for FT8
#define JT9_DEFAULT_FREQ 14078700UL
#define JT65_DEFAULT_FREQ 14078300UL
#define JT4_DEFAULT_FREQ 14078500UL
#define WSPR_DEFAULT_FREQ 14097200UL
#define FSQ_DEFAULT_FREQ 7105350UL // Base freq is 1350 Hz higher than dial freq in USB
#define FT8_DEFAULT_FREQ 14075000UL
#define DEFAULT_MODE MODE_JT65
// Hardware defines
#define BUTTON 12
#define LED_PIN 13
// Enumerations
enum mode {MODE_JT9, MODE_JT65, MODE_JT4, MODE_WSPR, MODE_FSQ_2, MODE_FSQ_3,
MODE_FSQ_4_5, MODE_FSQ_6, MODE_FT8};
// Class instantiation
Si5351 si5351;
JTEncode jtencode;
// Global variables
unsigned long freq;
char message[] = "N0CALL AA00";
char call[] = "N0CALL";
char loc[] = "AA00";
uint8_t dbm = 27;
uint8_t tx_buffer[255];
enum mode cur_mode = DEFAULT_MODE;
uint8_t symbol_count;
uint16_t tone_delay, tone_spacing;
// Loop through the string, transmitting one character at a time.
void encode()
{
uint8_t i;
// Reset the tone to the base frequency and turn on the output
si5351.output_enable(SI5351_CLK0, 1);
digitalWrite(LED_PIN, HIGH);
// Now transmit the channel symbols
if(cur_mode == MODE_FSQ_2 || cur_mode == MODE_FSQ_3 || cur_mode == MODE_FSQ_4_5 || cur_mode == MODE_FSQ_6)
{
uint8_t j = 0;
while(tx_buffer[j++] != 0xff);
symbol_count = j - 1;
}
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((freq * 100) + (tx_buffer[i] * tone_spacing), SI5351_CLK0);
delay(tone_delay);
}
// Turn off the output
si5351.output_enable(SI5351_CLK0, 0);
digitalWrite(LED_PIN, LOW);
}
void set_tx_buffer()
{
// Clear out the transmit buffer
memset(tx_buffer, 0, 255);
// Set the proper frequency and timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
jtencode.jt9_encode(message, tx_buffer);
break;
case MODE_JT65:
jtencode.jt65_encode(message, tx_buffer);
break;
case MODE_JT4:
jtencode.jt4_encode(message, tx_buffer);
break;
case MODE_WSPR:
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
break;
case MODE_FT8:
jtencode.ft8_encode(message, tx_buffer);
break;
case MODE_FSQ_2:
case MODE_FSQ_3:
case MODE_FSQ_4_5:
case MODE_FSQ_6:
jtencode.fsq_dir_encode(call, "n0call", ' ', "hello world", tx_buffer);
break;
}
}
void setup()
{
// Initialize the Si5351
// Change the 2nd parameter in init if using a ref osc other
// than 25 MHz
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Use the Arduino's on-board LED as a keying indicator.
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Use a button connected to pin 12 as a transmit trigger
pinMode(BUTTON, INPUT_PULLUP);
// Set the mode to use
cur_mode = MODE_JT65;
// Set the proper frequency, tone spacing, symbol count, and
// tone delay depending on mode
switch(cur_mode)
{
case MODE_JT9:
freq = JT9_DEFAULT_FREQ;
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
tone_spacing = JT9_TONE_SPACING;
tone_delay = JT9_DELAY;
break;
case MODE_JT65:
freq = JT65_DEFAULT_FREQ;
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
tone_spacing = JT65_TONE_SPACING;
tone_delay = JT65_DELAY;
break;
case MODE_JT4:
freq = JT4_DEFAULT_FREQ;
symbol_count = JT4_SYMBOL_COUNT; // From the library defines
tone_spacing = JT4_TONE_SPACING;
tone_delay = JT4_DELAY;
break;
case MODE_WSPR:
freq = WSPR_DEFAULT_FREQ;
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
tone_spacing = WSPR_TONE_SPACING;
tone_delay = WSPR_DELAY;
break;
case MODE_FT8:
freq = FT8_DEFAULT_FREQ;
symbol_count = FT8_SYMBOL_COUNT; // From the library defines
tone_spacing = FT8_TONE_SPACING;
tone_delay = FT8_DELAY;
break;
case MODE_FSQ_2:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_2_DELAY;
break;
case MODE_FSQ_3:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_3_DELAY;
break;
case MODE_FSQ_4_5:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_4_5_DELAY;
break;
case MODE_FSQ_6:
freq = FSQ_DEFAULT_FREQ;
tone_spacing = FSQ_TONE_SPACING;
tone_delay = FSQ_6_DELAY;
break;
}
// Set CLK0 output
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power if desired
si5351.output_enable(SI5351_CLK0, 0); // Disable the clock initially
// Encode the message in the transmit buffer
// This is RAM intensive and should be done separately from other subroutines
set_tx_buffer();
}
void loop()
{
// Debounce the button and trigger TX on push
if(digitalRead(BUTTON) == LOW)
{
delay(50); // delay to debounce
if (digitalRead(BUTTON) == LOW)
{
encode();
delay(50); //delay to avoid extra triggers
}
}
}

Wyświetl plik

@ -0,0 +1,22 @@
JTEncode KEYWORD1
jt65_encode KEYWORD2
jt9_encode KEYWORD2
jt4_encode KEYWORD2
wspr_encode KEYWORD2
ft8_encode KEYWORD2
fsq_encode KEYWORD2
fsq_dir_encode KEYWORD2
JT65_SYMBOL_COUNT LITERAL1
JT9_SYMBOL_COUNT LITERAL1
JT4_SYMBOL_COUNT LITERAL1
WSPR_SYMBOL_COUNT LITERAL1
FT8_SYMBOL_COUNT LITERAL1
JT65_ENCODE_COUNT LITERAL1
JT9_ENCODE_COUNT LITERAL1
FT8_ENCODE_COUNT LITERAL1
JT9_BIT_COUNT LITERAL1
JT4_BIT_COUNT LITERAL1
WSPR_BIT_COUNT LITERAL1
FT8_BIT_COUNT LITERAL1

Wyświetl plik

@ -0,0 +1,9 @@
name=Etherkit JTEncode
version=1.2.0
author=Jason Milldrum <milldrum@gmail.com>
maintainer=Jason Milldrum <milldrum@gmail.com>
sentence=Generate JT65, JT9, JT4, FT8, WSPR, and FSQ symbols on your Arduino.
paragraph=This library very simply generates a set of channel symbols for JT65, JT9, JT4, FT8, or WSPR based on the user providing a properly formatted Type 6 message for JT65, JT9, or JT4 (which is 13 valid characters), Type 0.0 or 0.5 message for FT8 (v2.0.0 protocol) or a callsign, Maidenhead grid locator, and power output for WSPR. It will also generate an arbitrary FSQ message of up to 200 characters in both directed and non-directed format. When paired with a synthesizer that can output frequencies in fine, phase-continuous tuning steps (such as the Si5351), then a beacon or telemetry transmitter can be created which can change the transmitted characters as needed from the Arduino.
category=Data Processing
url=https://github.com/etherkit/JTEncode
architectures=*

Wyświetl plik

@ -0,0 +1,263 @@
/*
* JTEncode.h - JT65/JT9/WSPR/FSQ encoder library for Arduino
*
* Copyright (C) 2015-2018 Jason Milldrum <milldrum@gmail.com>
*
* Based on the algorithms presented in the WSJT software suite.
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
* process that helped me to understand all of this.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef JTENCODE_H
#define JTENCODE_H
#include "int.h"
#include "rs_common.h"
#include "Arduino.h"
#include <stdint.h>
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
#include <avr/pgmspace.h>
#endif
#define JT65_SYMBOL_COUNT 126
#define JT9_SYMBOL_COUNT 85
#define JT4_SYMBOL_COUNT 207
#define WSPR_SYMBOL_COUNT 162
#define FT8_SYMBOL_COUNT 79
#define JT65_ENCODE_COUNT 63
#define JT9_ENCODE_COUNT 69
#define FT8_ENCODE_COUNT 77
#define JT9_BIT_COUNT 206
#define JT4_BIT_COUNT 206
#define WSPR_BIT_COUNT 162
#define FT8_BIT_COUNT 174
// Define the structure of a varicode table
typedef struct fsq_varicode
{
uint8_t ch;
uint8_t var[2];
} Varicode;
// The FSQ varicode table, based on the FSQ Varicode V3.0
// document provided by Murray Greenman, ZL1BPU
const Varicode fsq_code_table[] PROGMEM =
{
{' ', {00, 00}}, // space
{'!', {11, 30}},
{'"', {12, 30}},
{'#', {13, 30}},
{'$', {14, 30}},
{'%', {15, 30}},
{'&', {16, 30}},
{'\'', {17, 30}},
{'(', {18, 30}},
{')', {19, 30}},
{'*', {20, 30}},
{'+', {21, 30}},
{',', {27, 29}},
{'-', {22, 30}},
{'.', {27, 00}},
{'/', {23, 30}},
{'0', {10, 30}},
{'1', {01, 30}},
{'2', {02, 30}},
{'3', {03, 30}},
{'4', {04, 30}},
{'5', {05, 30}},
{'6', {06, 30}},
{'7', {07, 30}},
{'8', {8, 30}},
{'9', {9, 30}},
{':', {24, 30}},
{';', {25, 30}},
{'<', {26, 30}},
{'=', {00, 31}},
{'>', {27, 30}},
{'?', {28, 29}},
{'@', {00, 29}},
{'A', {01, 29}},
{'B', {02, 29}},
{'C', {03, 29}},
{'D', {04, 29}},
{'E', {05, 29}},
{'F', {06, 29}},
{'G', {07, 29}},
{'H', {8, 29}},
{'I', {9, 29}},
{'J', {10, 29}},
{'K', {11, 29}},
{'L', {12, 29}},
{'M', {13, 29}},
{'N', {14, 29}},
{'O', {15, 29}},
{'P', {16, 29}},
{'Q', {17, 29}},
{'R', {18, 29}},
{'S', {19, 29}},
{'T', {20, 29}},
{'U', {21, 29}},
{'V', {22, 29}},
{'W', {23, 29}},
{'X', {24, 29}},
{'Y', {25, 29}},
{'Z', {26, 29}},
{'[', {01, 31}},
{'\\', {02, 31}},
{']', {03, 31}},
{'^', {04, 31}},
{'_', {05, 31}},
{'`', {9, 31}},
{'a', {01, 00}},
{'b', {02, 00}},
{'c', {03, 00}},
{'d', {04, 00}},
{'e', {05, 00}},
{'f', {06, 00}},
{'g', {07, 00}},
{'h', {8, 00}},
{'i', {9, 00}},
{'j', {10, 00}},
{'k', {11, 00}},
{'l', {12, 00}},
{'m', {13, 00}},
{'n', {14, 00}},
{'o', {15, 00}},
{'p', {16, 00}},
{'q', {17, 00}},
{'r', {18, 00}},
{'s', {19, 00}},
{'t', {20, 00}},
{'u', {21, 00}},
{'v', {22, 00}},
{'w', {23, 00}},
{'x', {24, 00}},
{'y', {25, 00}},
{'z', {26, 00}},
{'{', {06, 31}},
{'|', {07, 31}},
{'}', {8, 31}},
{'~', {00, 30}},
{127, {28, 31}}, // DEL
{13, {28, 00}}, // CR
{10, {28, 00}}, // LF
{0, {28, 30}}, // IDLE
{241, {10, 31}}, // plus/minus
{246, {11, 31}}, // division sign
{248, {12, 31}}, // degrees sign
{158, {13, 31}}, // multiply sign
{156, {14, 31}}, // pound sterling sign
{8, {27, 31}} // BS
};
const uint8_t crc8_table[] PROGMEM = {
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
0xfa, 0xfd, 0xf4, 0xf3
};
const uint8_t jt9i[JT9_BIT_COUNT] PROGMEM = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0x10, 0x90, 0x50, 0x30, 0xb0, 0x70,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0x18, 0x98, 0x58, 0x38, 0xb8, 0x78,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0x14, 0x94, 0x54, 0x34, 0xb4, 0x74,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0x1c, 0x9c, 0x5c, 0x3c, 0xbc, 0x7c,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0x12, 0x92, 0x52, 0x32, 0xb2, 0x72,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0x1a, 0x9a, 0x5a, 0x3a, 0xba, 0x7a,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0x16, 0x96, 0x56, 0x36, 0xb6, 0x76,
0x0e, 0x8e, 0x4e, 0x2e, 0xae, 0x6e, 0x1e, 0x9e, 0x5e, 0x3e, 0xbe, 0x7e, 0x01,
0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0x11, 0x91, 0x51, 0x31, 0xb1, 0x71, 0x09,
0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0x19, 0x99, 0x59, 0x39, 0xb9, 0x79, 0x05,
0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0x15, 0x95, 0x55, 0x35, 0xb5, 0x75, 0x0d,
0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0x1d, 0x9d, 0x5d, 0x3d, 0xbd, 0x7d, 0x03,
0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0x13, 0x93, 0x53, 0x33, 0xb3, 0x73, 0x0b,
0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0x1b, 0x9b, 0x5b, 0x3b, 0xbb, 0x7b, 0x07,
0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0x17, 0x97, 0x57, 0x37, 0xb7, 0x77, 0x0f,
0x8f, 0x4f, 0x2f, 0xaf, 0x6f, 0x1f, 0x9f, 0x5f, 0x3f, 0xbf, 0x7f
};
class JTEncode
{
public:
JTEncode(void);
void jt65_encode(const char *, uint8_t *);
void jt9_encode(const char *, uint8_t *);
void jt4_encode(const char *, uint8_t *);
void wspr_encode(const char *, const char *, const uint8_t, uint8_t *);
void fsq_encode(const char *, const char *, uint8_t *);
void fsq_dir_encode(const char *, const char *, const char, const char *, uint8_t *);
void ft8_encode(const char *, uint8_t *);
private:
uint8_t jt_code(char);
uint8_t ft_code(char);
uint8_t wspr_code(char);
uint8_t gray_code(uint8_t);
int8_t hex2int(char);
void jt_message_prep(char *);
void ft_message_prep(char *);
void wspr_message_prep(char *, char *, uint8_t);
void jt65_bit_packing(char *, uint8_t *);
void jt9_bit_packing(char *, uint8_t *);
void wspr_bit_packing(uint8_t *);
void ft8_bit_packing(char*, uint8_t*);
void jt65_interleave(uint8_t *);
void jt9_interleave(uint8_t *);
void wspr_interleave(uint8_t *);
void jt9_packbits(uint8_t *, uint8_t *);
void jt_gray_code(uint8_t *, uint8_t);
void ft8_encode(uint8_t*, uint8_t*);
void jt65_merge_sync_vector(uint8_t *, uint8_t *);
void jt9_merge_sync_vector(uint8_t *, uint8_t *);
void jt4_merge_sync_vector(uint8_t *, uint8_t *);
void wspr_merge_sync_vector(uint8_t *, uint8_t *);
void ft8_merge_sync_vector(uint8_t*, uint8_t*);
void convolve(uint8_t *, uint8_t *, uint8_t, uint8_t);
void rs_encode(uint8_t *, uint8_t *);
void encode_rs_int(void *,data_t *, data_t *);
void free_rs_int(void *);
void * init_rs_int(int, int, int, int, int, int);
uint8_t crc8(const char *);
void * rs_inst;
char callsign[7];
char locator[5];
uint8_t power;
};
#endif

Wyświetl plik

@ -0,0 +1,98 @@
/**
* \file
* Functions and types for CRC checks.
*
* Generated on Thu Dec 6 17:52:34 2018
* by pycrc v0.9.1, https://pycrc.org
* using the configuration:
* - Width = 14
* - Poly = 0x2757
* - XorIn = Undefined
* - ReflectIn = Undefined
* - XorOut = Undefined
* - ReflectOut = Undefined
* - Algorithm = bit-by-bit
*/
#include "crc14.h" /* include the header file generated with pycrc */
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
static crc_t crc_reflect(crc_t data, size_t data_len);
crc_t crc_reflect(crc_t data, size_t data_len)
{
unsigned int i;
crc_t ret;
ret = data & 0x01;
for (i = 1; i < data_len; i++) {
data >>= 1;
ret = (ret << 1) | (data & 0x01);
}
return ret;
}
crc_t crc_init(const crc_cfg_t *cfg)
{
unsigned int i;
bool bit;
crc_t crc = cfg->xor_in;
for (i = 0; i < 14; i++) {
bit = crc & 0x01;
if (bit) {
crc = ((crc ^ 0x2757) >> 1) | 0x2000;
} else {
crc >>= 1;
}
}
return crc & 0x3fff;
}
crc_t crc_update(const crc_cfg_t *cfg, crc_t crc, const void *data, size_t data_len)
{
const unsigned char *d = (const unsigned char *)data;
unsigned int i;
bool bit;
unsigned char c;
while (data_len--) {
if (cfg->reflect_in) {
c = crc_reflect(*d++, 8);
} else {
c = *d++;
}
for (i = 0; i < 8; i++) {
bit = crc & 0x2000;
crc = (crc << 1) | ((c >> (7 - i)) & 0x01);
if (bit) {
crc ^= 0x2757;
}
}
crc &= 0x3fff;
}
return crc & 0x3fff;
}
crc_t crc_finalize(const crc_cfg_t *cfg, crc_t crc)
{
unsigned int i;
bool bit;
for (i = 0; i < 14; i++) {
bit = crc & 0x2000;
crc <<= 1;
if (bit) {
crc ^= 0x2757;
}
}
if (cfg->reflect_out) {
crc = crc_reflect(crc, 14);
}
return (crc ^ cfg->xor_out) & 0x3fff;
}

Wyświetl plik

@ -0,0 +1,121 @@
/**
* \file
* Functions and types for CRC checks.
*
* Generated on Thu Dec 6 17:52:01 2018
* by pycrc v0.9.1, https://pycrc.org
* using the configuration:
* - Width = 14
* - Poly = 0x2757
* - XorIn = Undefined
* - ReflectIn = Undefined
* - XorOut = Undefined
* - ReflectOut = Undefined
* - Algorithm = bit-by-bit
*
* This file defines the functions crc_init(), crc_update() and crc_finalize().
*
* The crc_init() function returns the inital \c crc value and must be called
* before the first call to crc_update().
* Similarly, the crc_finalize() function must be called after the last call
* to crc_update(), before the \c crc is being used.
* is being used.
*
* The crc_update() function can be called any number of times (including zero
* times) in between the crc_init() and crc_finalize() calls.
*
* This pseudo-code shows an example usage of the API:
* \code{.c}
* crc_cfg_t cfg = {
* 0, // reflect_in
* 0, // xor_in
* 0, // reflect_out
* 0, // xor_out
* };
* crc_t crc;
* unsigned char data[MAX_DATA_LEN];
* size_t data_len;
*
* crc = crc_init(&cfg);
* while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) {
* crc = crc_update(&cfg, crc, data, data_len);
* }
* crc = crc_finalize(&cfg, crc);
* \endcode
*/
#ifndef CRC14_H
#define CRC14_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* The definition of the used algorithm.
*
* This is not used anywhere in the generated code, but it may be used by the
* application code to call algorithm-specific code, if desired.
*/
#define CRC_ALGO_BIT_BY_BIT 1
/**
* The type of the CRC values.
*
* This type must be big enough to contain at least 14 bits.
*/
typedef uint_fast16_t crc_t;
/**
* The configuration type of the CRC algorithm.
*/
typedef struct {
bool reflect_in; /*!< Whether the input shall be reflected or not */
crc_t xor_in; /*!< The initial value of the register */
bool reflect_out; /*!< Whether the output shall be reflected or not */
crc_t xor_out; /*!< The value which shall be XOR-ed to the final CRC value */
} crc_cfg_t;
/**
* Calculate the initial crc value.
*
* \param[in] cfg A pointer to an initialised crc_cfg_t structure.
* \return The initial crc value.
*/
crc_t crc_init(const crc_cfg_t *cfg);
/**
* Update the crc value with new data.
*
* \param[in] crc The current crc value.
* \param[in] cfg A pointer to an initialised crc_cfg_t structure.
* \param[in] data Pointer to a buffer of \a data_len bytes.
* \param[in] data_len Number of bytes in the \a data buffer.
* \return The updated crc value.
*/
crc_t crc_update(const crc_cfg_t *cfg, crc_t crc, const void *data, size_t data_len);
/**
* Calculate the final crc value.
*
* \param[in] cfg A pointer to an initialised crc_cfg_t structure.
* \param[in] crc The current crc value.
* \return The final crc value.
*/
crc_t crc_finalize(const crc_cfg_t *cfg, crc_t crc);
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif /* CRC14_H */

Wyświetl plik

@ -0,0 +1,58 @@
/* The guts of the Reed-Solomon encoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
*
* NN - the total number of symbols in a RS block. Integer variable or literal.
* PAD - the number of pad symbols in a block. Integer variable or literal.
* ALPHA_TO - The address of an array of NN elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* INDEX_OF - The address of an array of NN elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
* The memset() and memmove() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
* Copyright 2004, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef A0
#define A0 (NN) /* Special reserved value encoding zero in index form */
{
int i, j;
data_t feedback;
memset(parity,0,NROOTS*sizeof(data_t));
for(i=0;i<NN-NROOTS-PAD;i++){
feedback = INDEX_OF[data[i] ^ parity[0]];
if(feedback != A0){ /* feedback term is non-zero */
#ifdef UNNORMALIZED
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
* always be for the polynomials constructed by init_rs()
*/
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
#endif
for(j=1;j<NROOTS;j++)
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
}
/* Shift */
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
if(feedback != A0)
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
else
parity[NROOTS-1] = 0;
}
}

Wyświetl plik

@ -0,0 +1,70 @@
/* Reed-Solomon encoder
* Copyright 2003, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*
* Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework
*
* The guts of the Reed-Solomon encoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
*
* NN - the total number of symbols in a RS block. Integer variable or literal.
* PAD - the number of pad symbols in a block. Integer variable or literal.
* ALPHA_TO - The address of an array of NN elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* INDEX_OF - The address of an array of NN elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
* The memset() and memmove() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
*/
#include <string.h>
#include <JTEncode.h>
#include "int.h"
#include "rs_common.h"
void JTEncode::encode_rs_int(void *p, data_t *data, data_t *parity)
{
struct rs *rs = (struct rs *)p;
#undef A_0
#define A_0 (NN) /* Special reserved value encoding zero in index form */
{
int i, j;
data_t feedback;
memset(parity,0,NROOTS*sizeof(data_t));
for(i=0;i<NN-NROOTS-PAD;i++){
feedback = INDEX_OF[data[i] ^ parity[0]];
if(feedback != A_0){ /* feedback term is non-zero */
#ifdef UNNORMALIZED
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
* always be for the polynomials constructed by init_rs()
*/
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
#endif
for(j=1;j<NROOTS;j++)
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
}
/* Shift */
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
if(feedback != A_0)
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
else
parity[NROOTS-1] = 0;
}
}
}

Wyświetl plik

@ -0,0 +1,97 @@
#ifndef GENERATOR_H
#define GENERATOR_H
#include <stdint.h>
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
#include <avr/pgmspace.h>
#endif
const uint8_t generator_bits[83][12] PROGMEM =
{
{0b10000011, 0b00101001, 0b11001110, 0b00010001, 0b10111111, 0b00110001, 0b11101010, 0b11110101, 0b00001001, 0b11110010, 0b01111111, 0b11000000},
{0b01110110, 0b00011100, 0b00100110, 0b01001110, 0b00100101, 0b11000010, 0b01011001, 0b00110011, 0b01010100, 0b10010011, 0b00010011, 0b00100000},
{0b11011100, 0b00100110, 0b01011001, 0b00000010, 0b11111011, 0b00100111, 0b01111100, 0b01100100, 0b00010000, 0b10100001, 0b10111101, 0b11000000},
{0b00011011, 0b00111111, 0b01000001, 0b01111000, 0b01011000, 0b11001101, 0b00101101, 0b11010011, 0b00111110, 0b11000111, 0b11110110, 0b00100000},
{0b00001001, 0b11111101, 0b10100100, 0b11111110, 0b11100000, 0b01000001, 0b10010101, 0b11111101, 0b00000011, 0b01000111, 0b10000011, 0b10100000},
{0b00000111, 0b01111100, 0b11001100, 0b11000001, 0b00011011, 0b10001000, 0b01110011, 0b11101101, 0b01011100, 0b00111101, 0b01001000, 0b10100000},
{0b00101001, 0b10110110, 0b00101010, 0b11111110, 0b00111100, 0b10100000, 0b00110110, 0b11110100, 0b11111110, 0b00011010, 0b10011101, 0b10100000},
{0b01100000, 0b01010100, 0b11111010, 0b11110101, 0b11110011, 0b01011101, 0b10010110, 0b11010011, 0b10110000, 0b11001000, 0b11000011, 0b11100000},
{0b11100010, 0b00000111, 0b10011000, 0b11100100, 0b00110001, 0b00001110, 0b11101101, 0b00100111, 0b10001000, 0b01001010, 0b11101001, 0b00000000},
{0b01110111, 0b01011100, 0b10011100, 0b00001000, 0b11101000, 0b00001110, 0b00100110, 0b11011101, 0b10101110, 0b01010110, 0b00110001, 0b10000000},
{0b10110000, 0b10111000, 0b00010001, 0b00000010, 0b10001100, 0b00101011, 0b11111001, 0b10010111, 0b00100001, 0b00110100, 0b10000111, 0b11000000},
{0b00011000, 0b10100000, 0b11001001, 0b00100011, 0b00011111, 0b11000110, 0b00001010, 0b11011111, 0b01011100, 0b01011110, 0b10100011, 0b00100000},
{0b01110110, 0b01000111, 0b00011110, 0b10000011, 0b00000010, 0b10100000, 0b01110010, 0b00011110, 0b00000001, 0b10110001, 0b00101011, 0b10000000},
{0b11111111, 0b10111100, 0b11001011, 0b10000000, 0b11001010, 0b10000011, 0b01000001, 0b11111010, 0b11111011, 0b01000111, 0b10110010, 0b11100000},
{0b01100110, 0b10100111, 0b00101010, 0b00010101, 0b10001111, 0b10010011, 0b00100101, 0b10100010, 0b10111111, 0b01100111, 0b00010111, 0b00000000},
{0b11000100, 0b00100100, 0b00110110, 0b10001001, 0b11111110, 0b10000101, 0b10110001, 0b11000101, 0b00010011, 0b01100011, 0b10100001, 0b10000000},
{0b00001101, 0b11111111, 0b01110011, 0b10010100, 0b00010100, 0b11010001, 0b10100001, 0b10110011, 0b01001011, 0b00011100, 0b00100111, 0b00000000},
{0b00010101, 0b10110100, 0b10001000, 0b00110000, 0b01100011, 0b01101100, 0b10001011, 0b10011001, 0b10001001, 0b01001001, 0b01110010, 0b11100000},
{0b00101001, 0b10101000, 0b10011100, 0b00001101, 0b00111101, 0b11101000, 0b00011101, 0b01100110, 0b01010100, 0b10001001, 0b10110000, 0b11100000},
{0b01001111, 0b00010010, 0b01101111, 0b00110111, 0b11111010, 0b01010001, 0b11001011, 0b11100110, 0b00011011, 0b11010110, 0b10111001, 0b01000000},
{0b10011001, 0b11000100, 0b01110010, 0b00111001, 0b11010000, 0b11011001, 0b01111101, 0b00111100, 0b10000100, 0b11100000, 0b10010100, 0b00000000},
{0b00011001, 0b00011001, 0b10110111, 0b01010001, 0b00011001, 0b01110110, 0b01010110, 0b00100001, 0b10111011, 0b01001111, 0b00011110, 0b10000000},
{0b00001001, 0b11011011, 0b00010010, 0b11010111, 0b00110001, 0b11111010, 0b11101110, 0b00001011, 0b10000110, 0b11011111, 0b01101011, 0b10000000},
{0b01001000, 0b10001111, 0b11000011, 0b00111101, 0b11110100, 0b00111111, 0b10111101, 0b11101110, 0b10100100, 0b11101010, 0b11111011, 0b01000000},
{0b10000010, 0b01110100, 0b00100011, 0b11101110, 0b01000000, 0b10110110, 0b01110101, 0b11110111, 0b01010110, 0b11101011, 0b01011111, 0b11100000},
{0b10101011, 0b11100001, 0b10010111, 0b11000100, 0b10000100, 0b11001011, 0b01110100, 0b01110101, 0b01110001, 0b01000100, 0b10101001, 0b10100000},
{0b00101011, 0b01010000, 0b00001110, 0b01001011, 0b11000000, 0b11101100, 0b01011010, 0b01101101, 0b00101011, 0b11011011, 0b11011101, 0b00000000},
{0b11000100, 0b01110100, 0b10101010, 0b01010011, 0b11010111, 0b00000010, 0b00011000, 0b01110110, 0b00010110, 0b01101001, 0b00110110, 0b00000000},
{0b10001110, 0b10111010, 0b00011010, 0b00010011, 0b11011011, 0b00110011, 0b10010000, 0b10111101, 0b01100111, 0b00011000, 0b11001110, 0b11000000},
{0b01110101, 0b00111000, 0b01000100, 0b01100111, 0b00111010, 0b00100111, 0b01111000, 0b00101100, 0b11000100, 0b00100000, 0b00010010, 0b11100000},
{0b00000110, 0b11111111, 0b10000011, 0b10100001, 0b01000101, 0b11000011, 0b01110000, 0b00110101, 0b10100101, 0b11000001, 0b00100110, 0b10000000},
{0b00111011, 0b00110111, 0b01000001, 0b01111000, 0b01011000, 0b11001100, 0b00101101, 0b11010011, 0b00111110, 0b11000011, 0b11110110, 0b00100000},
{0b10011010, 0b01001010, 0b01011010, 0b00101000, 0b11101110, 0b00010111, 0b11001010, 0b10011100, 0b00110010, 0b01001000, 0b01000010, 0b11000000},
{0b10111100, 0b00101001, 0b11110100, 0b01100101, 0b00110000, 0b10011100, 0b10010111, 0b01111110, 0b10001001, 0b01100001, 0b00001010, 0b01000000},
{0b00100110, 0b01100011, 0b10101110, 0b01101101, 0b11011111, 0b10001011, 0b01011100, 0b11100010, 0b10111011, 0b00101001, 0b01001000, 0b10000000},
{0b01000110, 0b11110010, 0b00110001, 0b11101111, 0b11100100, 0b01010111, 0b00000011, 0b01001100, 0b00011000, 0b00010100, 0b01000001, 0b10000000},
{0b00111111, 0b10110010, 0b11001110, 0b10000101, 0b10101011, 0b11101001, 0b10110000, 0b11000111, 0b00101110, 0b00000110, 0b11111011, 0b11100000},
{0b11011110, 0b10000111, 0b01001000, 0b00011111, 0b00101000, 0b00101100, 0b00010101, 0b00111001, 0b01110001, 0b10100000, 0b10100010, 0b11100000},
{0b11111100, 0b11010111, 0b11001100, 0b11110010, 0b00111100, 0b01101001, 0b11111010, 0b10011001, 0b10111011, 0b10100001, 0b01000001, 0b00100000},
{0b11110000, 0b00100110, 0b00010100, 0b01000111, 0b11101001, 0b01001001, 0b00001100, 0b10101000, 0b11100100, 0b01110100, 0b11001110, 0b11000000},
{0b01000100, 0b00010000, 0b00010001, 0b01011000, 0b00011000, 0b00011001, 0b01101111, 0b10010101, 0b11001101, 0b11010111, 0b00000001, 0b00100000},
{0b00001000, 0b10001111, 0b11000011, 0b00011101, 0b11110100, 0b10111111, 0b10111101, 0b11100010, 0b10100100, 0b11101010, 0b11111011, 0b01000000},
{0b10111000, 0b11111110, 0b11110001, 0b10110110, 0b00110000, 0b01110111, 0b00101001, 0b11111011, 0b00001010, 0b00000111, 0b10001100, 0b00000000},
{0b01011010, 0b11111110, 0b10100111, 0b10101100, 0b11001100, 0b10110111, 0b01111011, 0b10111100, 0b10011101, 0b10011001, 0b10101001, 0b00000000},
{0b01001001, 0b10100111, 0b00000001, 0b01101010, 0b11000110, 0b01010011, 0b11110110, 0b01011110, 0b11001101, 0b11001001, 0b00000111, 0b01100000},
{0b00011001, 0b01000100, 0b11010000, 0b10000101, 0b10111110, 0b01001110, 0b01111101, 0b10101000, 0b11010110, 0b11001100, 0b01111101, 0b00000000},
{0b00100101, 0b00011111, 0b01100010, 0b10101101, 0b11000100, 0b00000011, 0b00101111, 0b00001110, 0b11100111, 0b00010100, 0b00000000, 0b00100000},
{0b01010110, 0b01000111, 0b00011111, 0b10000111, 0b00000010, 0b10100000, 0b01110010, 0b00011110, 0b00000000, 0b10110001, 0b00101011, 0b10000000},
{0b00101011, 0b10001110, 0b01001001, 0b00100011, 0b11110010, 0b11011101, 0b01010001, 0b11100010, 0b11010101, 0b00110111, 0b11111010, 0b00000000},
{0b01101011, 0b01010101, 0b00001010, 0b01000000, 0b10100110, 0b01101111, 0b01000111, 0b01010101, 0b11011110, 0b10010101, 0b11000010, 0b01100000},
{0b10100001, 0b10001010, 0b11010010, 0b10001101, 0b01001110, 0b00100111, 0b11111110, 0b10010010, 0b10100100, 0b11110110, 0b11001000, 0b01000000},
{0b00010000, 0b11000010, 0b11100101, 0b10000110, 0b00111000, 0b10001100, 0b10111000, 0b00101010, 0b00111101, 0b10000000, 0b01110101, 0b10000000},
{0b11101111, 0b00110100, 0b10100100, 0b00011000, 0b00010111, 0b11101110, 0b00000010, 0b00010011, 0b00111101, 0b10110010, 0b11101011, 0b00000000},
{0b01111110, 0b10011100, 0b00001100, 0b01010100, 0b00110010, 0b01011010, 0b10011100, 0b00010101, 0b10000011, 0b01101110, 0b00000000, 0b00000000},
{0b00110110, 0b10010011, 0b11100101, 0b01110010, 0b11010001, 0b11111101, 0b11100100, 0b11001101, 0b11110000, 0b01111001, 0b11101000, 0b01100000},
{0b10111111, 0b10110010, 0b11001110, 0b11000101, 0b10101011, 0b11100001, 0b10110000, 0b11000111, 0b00101110, 0b00000111, 0b11111011, 0b11100000},
{0b01111110, 0b11100001, 0b10000010, 0b00110000, 0b11000101, 0b10000011, 0b11001100, 0b11001100, 0b01010111, 0b11010100, 0b10110000, 0b10000000},
{0b10100000, 0b01100110, 0b11001011, 0b00101111, 0b11101101, 0b10101111, 0b11001001, 0b11110101, 0b00100110, 0b01100100, 0b00010010, 0b01100000},
{0b10111011, 0b00100011, 0b01110010, 0b01011010, 0b10111100, 0b01000111, 0b11001100, 0b01011111, 0b01001100, 0b11000100, 0b11001101, 0b00100000},
{0b11011110, 0b11011001, 0b11011011, 0b10100011, 0b10111110, 0b11100100, 0b00001100, 0b01011001, 0b10110101, 0b01100000, 0b10011011, 0b01000000},
{0b11011001, 0b10100111, 0b00000001, 0b01101010, 0b11000110, 0b01010011, 0b11100110, 0b11011110, 0b11001101, 0b11001001, 0b00000011, 0b01100000},
{0b10011010, 0b11010100, 0b01101010, 0b11101101, 0b01011111, 0b01110000, 0b01111111, 0b00101000, 0b00001010, 0b10110101, 0b11111100, 0b01000000},
{0b11100101, 0b10010010, 0b00011100, 0b01110111, 0b10000010, 0b00100101, 0b10000111, 0b00110001, 0b01101101, 0b01111101, 0b00111100, 0b00100000},
{0b01001111, 0b00010100, 0b11011010, 0b10000010, 0b01000010, 0b10101000, 0b10111000, 0b01101101, 0b11001010, 0b01110011, 0b00110101, 0b00100000},
{0b10001011, 0b10001011, 0b01010000, 0b01111010, 0b11010100, 0b01100111, 0b11010100, 0b01000100, 0b00011101, 0b11110111, 0b01110000, 0b11100000},
{0b00100010, 0b10000011, 0b00011100, 0b10011100, 0b11110001, 0b00010110, 0b10010100, 0b01100111, 0b10101101, 0b00000100, 0b10110110, 0b10000000},
{0b00100001, 0b00111011, 0b10000011, 0b10001111, 0b11100010, 0b10101110, 0b01010100, 0b11000011, 0b10001110, 0b11100111, 0b00011000, 0b00000000},
{0b01011101, 0b10010010, 0b01101011, 0b01101101, 0b11010111, 0b00011111, 0b00001000, 0b01010001, 0b10000001, 0b10100100, 0b11100001, 0b00100000},
{0b01100110, 0b10101011, 0b01111001, 0b11010100, 0b10110010, 0b10011110, 0b11100110, 0b11100110, 0b10010101, 0b00001001, 0b11100101, 0b01100000},
{0b10010101, 0b10000001, 0b01001000, 0b01101000, 0b00101101, 0b01110100, 0b10001010, 0b00111000, 0b11011101, 0b01101000, 0b10111010, 0b10100000},
{0b10111000, 0b11001110, 0b00000010, 0b00001100, 0b11110000, 0b01101001, 0b11000011, 0b00101010, 0b01110010, 0b00111010, 0b10110001, 0b01000000},
{0b11110100, 0b00110011, 0b00011101, 0b01101101, 0b01000110, 0b00010110, 0b00000111, 0b11101001, 0b01010111, 0b01010010, 0b01110100, 0b01100000},
{0b01101101, 0b10100010, 0b00111011, 0b10100100, 0b00100100, 0b10111001, 0b01011001, 0b01100001, 0b00110011, 0b11001111, 0b10011100, 0b10000000},
{0b10100110, 0b00110110, 0b10111100, 0b10111100, 0b01111011, 0b00110000, 0b11000101, 0b11111011, 0b11101010, 0b11100110, 0b01111111, 0b11100000},
{0b01011100, 0b10110000, 0b11011000, 0b01101010, 0b00000111, 0b11011111, 0b01100101, 0b01001010, 0b10010000, 0b10001001, 0b10100010, 0b00000000},
{0b11110001, 0b00011111, 0b00010000, 0b01101000, 0b01001000, 0b01111000, 0b00001111, 0b11001001, 0b11101100, 0b11011101, 0b10000000, 0b10100000},
{0b00011111, 0b10111011, 0b01010011, 0b01100100, 0b11111011, 0b10001101, 0b00101100, 0b10011101, 0b01110011, 0b00001101, 0b01011011, 0b10100000},
{0b11111100, 0b10111000, 0b01101011, 0b11000111, 0b00001010, 0b01010000, 0b11001001, 0b11010000, 0b00101010, 0b01011101, 0b00000011, 0b01000000},
{0b10100101, 0b00110100, 0b01000011, 0b00110000, 0b00101001, 0b11101010, 0b11000001, 0b01011111, 0b00110010, 0b00101110, 0b00110100, 0b11000000},
{0b11001001, 0b10001001, 0b11011001, 0b11000111, 0b11000011, 0b11010011, 0b10111000, 0b11000101, 0b01011101, 0b01110101, 0b00010011, 0b00000000},
{0b01111011, 0b10110011, 0b10001011, 0b00101111, 0b00000001, 0b10000110, 0b11010100, 0b01100110, 0b01000011, 0b10101110, 0b10010110, 0b00100000},
{0b00100110, 0b01000100, 0b11101011, 0b10101101, 0b11101011, 0b01000100, 0b10111001, 0b01000110, 0b01111101, 0b00011111, 0b01000010, 0b11000000},
{0b01100000, 0b10001100, 0b11001000, 0b01010111, 0b01011001, 0b01001011, 0b11111011, 0b10110101, 0b01011101, 0b01101001, 0b01100000, 0b00000000}
};
#endif

Wyświetl plik

@ -0,0 +1,106 @@
/* Common code for intializing a Reed-Solomon control block (char or int symbols)
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef NULL
#define NULL ((void *)0)
//{
int i, j, sr,root,iprim;
rs = NULL;
/* Check parameter ranges */
if(symsize < 0 || symsize > 8*sizeof(data_t)){
goto done;
}
if(fcr < 0 || fcr >= (1<<symsize))
goto done;
if(prim <= 0 || prim >= (1<<symsize))
goto done;
if(nroots < 0 || nroots >= (1<<symsize))
goto done; /* Can't have more roots than symbol values! */
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
goto done; /* Too much padding */
rs = (struct rs *)calloc(1,sizeof(struct rs));
if(rs == NULL)
goto done;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->alpha_to == NULL){
free(rs);
rs = NULL;
goto done;
}
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->index_of == NULL){
free(rs->alpha_to);
free(rs);
rs = NULL;
goto done;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = A0; /* log(zero) = -inf */
rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
sr = 1;
for(i=0;i<rs->nn;i++){
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if(sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if(sr != 1){
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
if(rs->genpoly == NULL){
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--){
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
done:;
//}

Wyświetl plik

@ -0,0 +1,127 @@
/* Initialize a RS codec
*
* Copyright 2002 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*
* Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework
*/
#include <string.h>
#include <stdlib.h>
#include <JTEncode.h>
#include "rs_common.h"
void JTEncode::free_rs_int(void * p)
{
struct rs *rs = (struct rs *)p;
free(rs->alpha_to);
free(rs->index_of);
free(rs->genpoly);
free(rs);
}
void * JTEncode::init_rs_int(int symsize, int gfpoly, int fcr, int prim,
int nroots, int pad)
{
struct rs *rs;
int i, j, sr,root,iprim;
rs = ((struct rs *)0);
/* Check parameter ranges */
if(symsize < 0 || symsize > 8*sizeof(data_t)){
goto done;
}
if(fcr < 0 || fcr >= (1<<symsize))
goto done;
if(prim <= 0 || prim >= (1<<symsize))
goto done;
if(nroots < 0 || nroots >= (1<<symsize))
goto done; /* Can't have more roots than symbol values! */
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
goto done; /* Too much padding */
rs = (struct rs *)calloc(1,sizeof(struct rs));
if(rs == ((struct rs *)0))
goto done;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->alpha_to == NULL){
free(rs);
rs = ((struct rs *)0);
goto done;
}
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->index_of == NULL){
free(rs->alpha_to);
free(rs);
rs = ((struct rs *)0);
goto done;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = A_0; /* log(zero) = -inf */
rs->alpha_to[A_0] = 0; /* alpha**-inf = 0 */
sr = 1;
for(i=0;i<rs->nn;i++){
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if(sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if(sr != 1){
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = ((struct rs *)0);
goto done;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
if(rs->genpoly == NULL){
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = ((struct rs *)0);
goto done;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--){
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
done:;
return rs;
}

Wyświetl plik

@ -0,0 +1,27 @@
/* Stuff specific to the general (integer) version of the Reed-Solomon codecs
*
* Copyright 2003, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#ifndef INT_H_
#define INT_H_
#include <stdint.h>
typedef uint8_t data_t;
//typedef unsigned int data_t;
#define MODNN(x) modnn(rs,x)
#define MM (rs->mm)
#define NN (rs->nn)
#define ALPHA_TO (rs->alpha_to)
#define INDEX_OF (rs->index_of)
#define GENPOLY (rs->genpoly)
#define NROOTS (rs->nroots)
#define FCR (rs->fcr)
#define PRIM (rs->prim)
#define IPRIM (rs->iprim)
#define PAD (rs->pad)
#define A_0 (NN)
#endif

Wyświetl plik

@ -0,0 +1,33 @@
/* Stuff common to all the general-purpose Reed-Solomon codecs
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#ifndef RS_COMMON_H_
#define RS_COMMON_H_
#include "int.h"
/* Reed-Solomon codec control block */
struct rs {
int mm; /* Bits per symbol */
int nn; /* Symbols per block (= (1<<mm)-1) */
data_t *alpha_to; /* log lookup table */
data_t *index_of; /* Antilog lookup table */
data_t *genpoly; /* Generator polynomial */
int nroots; /* Number of generator roots = number of parity symbols */
int fcr; /* First consecutive root, index form */
int prim; /* Primitive element, index form */
int iprim; /* prim-th root of 1, index form */
int pad; /* Padding bytes in shortened block */
};
static inline int modnn(struct rs *rs,int x){
while (x >= rs->nn) {
x -= rs->nn;
x = (x >> rs->mm) + (x & rs->nn);
}
return x;
}
#endif

Wyświetl plik

@ -0,0 +1,475 @@
#include <string.h>
#include "AFSK.h"
#include "Arduino.h"
extern unsigned long custom_preamble;
extern unsigned long custom_tail;
extern int LibAPRS_vref;
extern bool LibAPRS_open_squelch;
bool hw_afsk_dac_isr = false;
bool hw_5v_ref = false;
Afsk *AFSK_modem;
// Forward declerations
int afsk_getchar(void);
void afsk_putchar(char c);
void AFSK_hw_refDetect(void) {
// This is manual for now
if (LibAPRS_vref == REF_5V) {
hw_5v_ref = true;
} else {
hw_5v_ref = false;
}
}
void AFSK_hw_init(void) {
// Set up ADC
AFSK_hw_refDetect();
TCCR1A = 0;
TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12);
ICR1 = (((CPU_FREQ+FREQUENCY_CORRECTION)) / 9600) - 1;
if (hw_5v_ref) {
ADMUX = _BV(REFS0) | 0;
} else {
ADMUX = 0;
}
ADC_DDR &= ~_BV(0);
ADC_PORT &= ~_BV(0);
DIDR0 |= _BV(0);
ADCSRB = _BV(ADTS2) |
_BV(ADTS1) |
_BV(ADTS0);
ADCSRA = _BV(ADEN) |
_BV(ADSC) |
_BV(ADATE)|
_BV(ADIE) |
_BV(ADPS2);
AFSK_DAC_INIT();
LED_TX_INIT();
LED_RX_INIT();
}
void AFSK_init(Afsk *afsk) {
// Allocate modem struct memory
memset(afsk, 0, sizeof(*afsk));
AFSK_modem = afsk;
// Set phase increment
afsk->phaseInc = MARK_INC;
// Initialise FIFO buffers
fifo_init(&afsk->delayFifo, (uint8_t *)afsk->delayBuf, sizeof(afsk->delayBuf));
fifo_init(&afsk->rxFifo, afsk->rxBuf, sizeof(afsk->rxBuf));
fifo_init(&afsk->txFifo, afsk->txBuf, sizeof(afsk->txBuf));
// Fill delay FIFO with zeroes
for (int i = 0; i<SAMPLESPERBIT / 2; i++) {
fifo_push(&afsk->delayFifo, 0);
}
AFSK_hw_init();
}
static void AFSK_txStart(Afsk *afsk) {
if (!afsk->sending) {
afsk->phaseInc = MARK_INC;
afsk->phaseAcc = 0;
afsk->bitstuffCount = 0;
afsk->sending = true;
LED_TX_ON();
afsk->preambleLength = DIV_ROUND(custom_preamble * BITRATE, 8000);
AFSK_DAC_IRQ_START();
}
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
afsk->tailLength = DIV_ROUND(custom_tail * BITRATE, 8000);
}
}
void afsk_putchar(char c) {
AFSK_txStart(AFSK_modem);
while(fifo_isfull_locked(&AFSK_modem->txFifo)) { /* Wait */ }
fifo_push_locked(&AFSK_modem->txFifo, c);
}
int afsk_getchar(void) {
if (fifo_isempty_locked(&AFSK_modem->rxFifo)) {
return EOF;
} else {
return fifo_pop_locked(&AFSK_modem->rxFifo);
}
}
void AFSK_transmit(char *buffer, size_t size) {
fifo_flush(&AFSK_modem->txFifo);
int i = 0;
while (size--) {
afsk_putchar(buffer[i++]);
}
}
uint8_t AFSK_dac_isr(Afsk *afsk) {
if (afsk->sampleIndex == 0) {
if (afsk->txBit == 0) {
if (fifo_isempty(&afsk->txFifo) && afsk->tailLength == 0) {
AFSK_DAC_IRQ_STOP();
afsk->sending = false;
LED_TX_OFF();
return 0;
} else {
if (!afsk->bitStuff) afsk->bitstuffCount = 0;
afsk->bitStuff = true;
if (afsk->preambleLength == 0) {
if (fifo_isempty(&afsk->txFifo)) {
afsk->tailLength--;
afsk->currentOutputByte = HDLC_FLAG;
} else {
afsk->currentOutputByte = fifo_pop(&afsk->txFifo);
}
} else {
afsk->preambleLength--;
afsk->currentOutputByte = HDLC_FLAG;
}
if (afsk->currentOutputByte == AX25_ESC) {
if (fifo_isempty(&afsk->txFifo)) {
AFSK_DAC_IRQ_STOP();
afsk->sending = false;
LED_TX_OFF();
return 0;
} else {
afsk->currentOutputByte = fifo_pop(&afsk->txFifo);
}
} else if (afsk->currentOutputByte == HDLC_FLAG || afsk->currentOutputByte == HDLC_RESET) {
afsk->bitStuff = false;
}
}
afsk->txBit = 0x01;
}
if (afsk->bitStuff && afsk->bitstuffCount >= BIT_STUFF_LEN) {
afsk->bitstuffCount = 0;
afsk->phaseInc = SWITCH_TONE(afsk->phaseInc);
} else {
if (afsk->currentOutputByte & afsk->txBit) {
afsk->bitstuffCount++;
} else {
afsk->bitstuffCount = 0;
afsk->phaseInc = SWITCH_TONE(afsk->phaseInc);
}
afsk->txBit <<= 1;
}
afsk->sampleIndex = SAMPLESPERBIT;
}
afsk->phaseAcc += afsk->phaseInc;
afsk->phaseAcc %= SIN_LEN;
afsk->sampleIndex--;
return sinSample(afsk->phaseAcc);
}
static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo) {
// Initialise a return value. We start with the
// assumption that all is going to end well :)
bool ret = true;
// Bitshift our byte of demodulated bits to
// the left by one bit, to make room for the
// next incoming bit
hdlc->demodulatedBits <<= 1;
// And then put the newest bit from the
// demodulator into the byte.
hdlc->demodulatedBits |= bit ? 1 : 0;
// Now we'll look at the last 8 received bits, and
// check if we have received a HDLC flag (01111110)
if (hdlc->demodulatedBits == HDLC_FLAG) {
// If we have, check that our output buffer is
// not full.
if (!fifo_isfull(fifo)) {
// If it isn't, we'll push the HDLC_FLAG into
// the buffer and indicate that we are now
// receiving data. For bling we also turn
// on the RX LED.
fifo_push(fifo, HDLC_FLAG);
hdlc->receiving = true;
if(!LibAPRS_open_squelch) {
LED_RX_ON();
}
} else {
// If the buffer is full, we have a problem
// and abort by setting the return value to
// false and stopping the here.
ret = false;
hdlc->receiving = false;
LED_RX_OFF();
}
// Everytime we receive a HDLC_FLAG, we reset the
// storage for our current incoming byte and bit
// position in that byte. This effectively
// synchronises our parsing to the start and end
// of the received bytes.
hdlc->currentByte = 0;
hdlc->bitIndex = 0;
return ret;
}
// Check if we have received a RESET flag (01111111)
// In this comparison we also detect when no transmission
// (or silence) is taking place, and the demodulator
// returns an endless stream of zeroes. Due to the NRZ
// coding, the actual bits send to this function will
// be an endless stream of ones, which this AND operation
// will also detect.
if ((hdlc->demodulatedBits & HDLC_RESET) == HDLC_RESET) {
// If we have, something probably went wrong at the
// transmitting end, and we abort the reception.
hdlc->receiving = false;
LED_RX_OFF();
return ret;
}
// If we have not yet seen a HDLC_FLAG indicating that
// a transmission is actually taking place, don't bother
// with anything.
if (!hdlc->receiving)
return ret;
// First check if what we are seeing is a stuffed bit.
// Since the different HDLC control characters like
// HDLC_FLAG, HDLC_RESET and such could also occur in
// a normal data stream, we employ a method known as
// "bit stuffing". All control characters have more than
// 5 ones in a row, so if the transmitting party detects
// this sequence in the _data_ to be transmitted, it inserts
// a zero to avoid the receiving party interpreting it as
// a control character. Therefore, if we detect such a
// "stuffed bit", we simply ignore it and wait for the
// next bit to come in.
//
// We do the detection by applying an AND bit-mask to the
// stream of demodulated bits. This mask is 00111111 (0x3f)
// if the result of the operation is 00111110 (0x3e), we
// have detected a stuffed bit.
if ((hdlc->demodulatedBits & 0x3f) == 0x3e)
return ret;
// If we have an actual 1 bit, push this to the current byte
// If it's a zero, we don't need to do anything, since the
// bit is initialized to zero when we bitshifted earlier.
if (hdlc->demodulatedBits & 0x01)
hdlc->currentByte |= 0x80;
// Increment the bitIndex and check if we have a complete byte
if (++hdlc->bitIndex >= 8) {
// If we have a HDLC control character, put a AX.25 escape
// in the received data. We know we need to do this,
// because at this point we must have already seen a HDLC
// flag, meaning that this control character is the result
// of a bitstuffed byte that is equal to said control
// character, but is actually part of the data stream.
// By inserting the escape character, we tell the protocol
// layer that this is not an actual control character, but
// data.
if ((hdlc->currentByte == HDLC_FLAG ||
hdlc->currentByte == HDLC_RESET ||
hdlc->currentByte == AX25_ESC)) {
// We also need to check that our received data buffer
// is not full before putting more data in
if (!fifo_isfull(fifo)) {
fifo_push(fifo, AX25_ESC);
} else {
// If it is, abort and return false
hdlc->receiving = false;
LED_RX_OFF();
ret = false;
}
}
// Push the actual byte to the received data FIFO,
// if it isn't full.
if (!fifo_isfull(fifo)) {
fifo_push(fifo, hdlc->currentByte);
} else {
// If it is, well, you know by now!
hdlc->receiving = false;
LED_RX_OFF();
ret = false;
}
// Wipe received byte and reset bit index to 0
hdlc->currentByte = 0;
hdlc->bitIndex = 0;
} else {
// We don't have a full byte yet, bitshift the byte
// to make room for the next bit
hdlc->currentByte >>= 1;
}
//digitalWrite(13, LOW);
return ret;
}
void AFSK_adc_isr(Afsk *afsk, int8_t currentSample) {
// To determine the received frequency, and thereby
// the bit of the sample, we multiply the sample by
// a sample delayed by (samples per bit / 2).
// We then lowpass-filter the samples with a
// Chebyshev filter. The lowpass filtering serves
// to "smooth out" the variations in the samples.
afsk->iirX[0] = afsk->iirX[1];
afsk->iirX[1] = ((int8_t)fifo_pop(&afsk->delayFifo) * currentSample) >> 2;
afsk->iirY[0] = afsk->iirY[1];
afsk->iirY[1] = afsk->iirX[0] + afsk->iirX[1] + (afsk->iirY[0] >> 1); // Chebyshev filter
// We put the sampled bit in a delay-line:
// First we bitshift everything 1 left
afsk->sampledBits <<= 1;
// And then add the sampled bit to our delay line
afsk->sampledBits |= (afsk->iirY[1] > 0) ? 1 : 0;
// Put the current raw sample in the delay FIFO
fifo_push(&afsk->delayFifo, currentSample);
// We need to check whether there is a signal transition.
// If there is, we can recalibrate the phase of our
// sampler to stay in sync with the transmitter. A bit of
// explanation is required to understand how this works.
// Since we have PHASE_MAX/PHASE_BITS = 8 samples per bit,
// we employ a phase counter (currentPhase), that increments
// by PHASE_BITS everytime a sample is captured. When this
// counter reaches PHASE_MAX, it wraps around by modulus
// PHASE_MAX. We then look at the last three samples we
// captured and determine if the bit was a one or a zero.
//
// This gives us a "window" looking into the stream of
// samples coming from the ADC. Sort of like this:
//
// Past Future
// 0000000011111111000000001111111100000000
// |________|
// ||
// Window
//
// Every time we detect a signal transition, we adjust
// where this window is positioned little. How much we
// adjust it is defined by PHASE_INC. If our current phase
// phase counter value is less than half of PHASE_MAX (ie,
// the window size) when a signal transition is detected,
// add PHASE_INC to our phase counter, effectively moving
// the window a little bit backward (to the left in the
// illustration), inversely, if the phase counter is greater
// than half of PHASE_MAX, we move it forward a little.
// This way, our "window" is constantly seeking to position
// it's center at the bit transitions. Thus, we synchronise
// our timing to the transmitter, even if it's timing is
// a little off compared to our own.
if (SIGNAL_TRANSITIONED(afsk->sampledBits)) {
if (afsk->currentPhase < PHASE_THRESHOLD) {
afsk->currentPhase += PHASE_INC;
} else {
afsk->currentPhase -= PHASE_INC;
}
}
// We increment our phase counter
afsk->currentPhase += PHASE_BITS;
// Check if we have reached the end of
// our sampling window.
if (afsk->currentPhase >= PHASE_MAX) {
// If we have, wrap around our phase
// counter by modulus
afsk->currentPhase %= PHASE_MAX;
// Bitshift to make room for the next
// bit in our stream of demodulated bits
afsk->actualBits <<= 1;
// We determine the actual bit value by reading
// the last 3 sampled bits. If there is three or
// more 1's, we will assume that the transmitter
// sent us a one, otherwise we assume a zero
uint8_t bits = afsk->sampledBits & 0x07;
if (bits == 0x07 || // 111
bits == 0x06 || // 110
bits == 0x05 || // 101
bits == 0x03 // 011
) {
afsk->actualBits |= 1;
}
//// Alternative using five bits ////////////////
// uint8_t bits = afsk->sampledBits & 0x0f;
// uint8_t c = 0;
// c += bits & BV(1);
// c += bits & BV(2);
// c += bits & BV(3);
// c += bits & BV(4);
// c += bits & BV(5);
// if (c >= 3) afsk->actualBits |= 1;
/////////////////////////////////////////////////
// Now we can pass the actual bit to the HDLC parser.
// We are using NRZ coding, so if 2 consecutive bits
// have the same value, we have a 1, otherwise a 0.
// We use the TRANSITION_FOUND function to determine this.
//
// This is smart in combination with bit stuffing,
// since it ensures a transmitter will never send more
// than five consecutive 1's. When sending consecutive
// ones, the signal stays at the same level, and if
// this happens for longer periods of time, we would
// not be able to synchronize our phase to the transmitter
// and would start experiencing "bit slip".
//
// By combining bit-stuffing with NRZ coding, we ensure
// that the signal will regularly make transitions
// that we can use to synchronize our phase.
//
// We also check the return of the Link Control parser
// to check if an error occured.
if (!hdlcParse(&afsk->hdlc, !TRANSITION_FOUND(afsk->actualBits), &afsk->rxFifo)) {
afsk->status |= 1;
if (fifo_isfull(&afsk->rxFifo)) {
fifo_flush(&afsk->rxFifo);
afsk->status = 0;
}
}
}
}
extern void APRS_poll();
uint8_t poll_timer = 0;
ISR(ADC_vect) {
TIFR1 = _BV(ICF1);
AFSK_adc_isr(AFSK_modem, ((int16_t)((ADC) >> 2) - 128));
if (hw_afsk_dac_isr) {
DAC_PORT = (AFSK_dac_isr(AFSK_modem) & 0xF0) | _BV(3);
} else {
DAC_PORT = 128;
}
poll_timer++;
if (poll_timer > 3) {
poll_timer = 0;
APRS_poll();
}
}

Wyświetl plik

@ -0,0 +1,138 @@
#ifndef AFSK_H
#define AFSK_H
#include "device.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include "FIFO.h"
#include "HDLC.h"
#define SIN_LEN 512
static const uint8_t sin_table[] PROGMEM =
{
128, 129, 131, 132, 134, 135, 137, 138, 140, 142, 143, 145, 146, 148, 149, 151,
152, 154, 155, 157, 158, 160, 162, 163, 165, 166, 167, 169, 170, 172, 173, 175,
176, 178, 179, 181, 182, 183, 185, 186, 188, 189, 190, 192, 193, 194, 196, 197,
198, 200, 201, 202, 203, 205, 206, 207, 208, 210, 211, 212, 213, 214, 215, 217,
218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, 243, 244, 245,
245, 246, 246, 247, 248, 248, 249, 249, 250, 250, 250, 251, 251, 252, 252, 252,
253, 253, 253, 253, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255,
};
inline static uint8_t sinSample(uint16_t i) {
uint16_t newI = i % (SIN_LEN/2);
newI = (newI >= (SIN_LEN/4)) ? (SIN_LEN/2 - newI -1) : newI;
uint8_t sine = pgm_read_byte(&sin_table[newI]);
return (i >= (SIN_LEN/2)) ? (255 - sine) : sine;
}
#define SWITCH_TONE(inc) (((inc) == MARK_INC) ? SPACE_INC : MARK_INC)
#define BITS_DIFFER(bits1, bits2) (((bits1)^(bits2)) & 0x01)
#define DUAL_XOR(bits1, bits2) ((((bits1)^(bits2)) & 0x03) == 0x03)
#define SIGNAL_TRANSITIONED(bits) DUAL_XOR((bits), (bits) >> 2)
#define TRANSITION_FOUND(bits) BITS_DIFFER((bits), (bits) >> 1)
#define CPU_FREQ F_CPU
#define CONFIG_AFSK_RX_BUFLEN 64
#define CONFIG_AFSK_TX_BUFLEN 64
#define CONFIG_AFSK_RXTIMEOUT 0
#define CONFIG_AFSK_PREAMBLE_LEN 150UL
#define CONFIG_AFSK_TRAILER_LEN 50UL
#define SAMPLERATE 9600
#define BITRATE 1200
#define SAMPLESPERBIT (SAMPLERATE / BITRATE)
#define BIT_STUFF_LEN 5
#define MARK_FREQ 1200
#define SPACE_FREQ 2200
#define PHASE_BITS 8 // How much to increment phase counter each sample
#define PHASE_INC 1 // Nudge by an eigth of a sample each adjustment
#define PHASE_MAX (SAMPLESPERBIT * PHASE_BITS) // Resolution of our phase counter = 64
#define PHASE_THRESHOLD (PHASE_MAX / 2) // Target transition point of our phase window
typedef struct Hdlc
{
uint8_t demodulatedBits;
uint8_t bitIndex;
uint8_t currentByte;
bool receiving;
} Hdlc;
typedef struct Afsk
{
// Stream access to modem
FILE fd;
// General values
Hdlc hdlc; // We need a link control structure
uint16_t preambleLength; // Length of sync preamble
uint16_t tailLength; // Length of transmission tail
// Modulation values
uint8_t sampleIndex; // Current sample index for outgoing bit
uint8_t currentOutputByte; // Current byte to be modulated
uint8_t txBit; // Mask of current modulated bit
bool bitStuff; // Whether bitstuffing is allowed
uint8_t bitstuffCount; // Counter for bit-stuffing
uint16_t phaseAcc; // Phase accumulator
uint16_t phaseInc; // Phase increment per sample
FIFOBuffer txFifo; // FIFO for transmit data
uint8_t txBuf[CONFIG_AFSK_TX_BUFLEN]; // Actial data storage for said FIFO
volatile bool sending; // Set when modem is sending
// Demodulation values
FIFOBuffer delayFifo; // Delayed FIFO for frequency discrimination
int8_t delayBuf[SAMPLESPERBIT / 2 + 1]; // Actual data storage for said FIFO
FIFOBuffer rxFifo; // FIFO for received data
uint8_t rxBuf[CONFIG_AFSK_RX_BUFLEN]; // Actual data storage for said FIFO
int16_t iirX[2]; // IIR Filter X cells
int16_t iirY[2]; // IIR Filter Y cells
uint8_t sampledBits; // Bits sampled by the demodulator (at ADC speed)
int8_t currentPhase; // Current phase of the demodulator
uint8_t actualBits; // Actual found bits at correct bitrate
volatile int status; // Status of the modem, 0 means OK
} Afsk;
#define DIV_ROUND(dividend, divisor) (((dividend) + (divisor) / 2) / (divisor))
#define MARK_INC (uint16_t)(DIV_ROUND(SIN_LEN * (uint32_t)MARK_FREQ, CONFIG_AFSK_DAC_SAMPLERATE))
#define SPACE_INC (uint16_t)(DIV_ROUND(SIN_LEN * (uint32_t)SPACE_FREQ, CONFIG_AFSK_DAC_SAMPLERATE))
#define AFSK_DAC_IRQ_START() do { extern bool hw_afsk_dac_isr; hw_afsk_dac_isr = true; } while (0)
#define AFSK_DAC_IRQ_STOP() do { extern bool hw_afsk_dac_isr; hw_afsk_dac_isr = false; } while (0)
#define AFSK_DAC_INIT() do { DAC_DDR |= 0xF8; } while (0)
// Here's some macros for controlling the RX/TX LEDs
// THE _INIT() functions writes to the DDRB register
// to configure the pins as output pins, and the _ON()
// and _OFF() functions writes to the PORT registers
// to turn the pins on or off.
#define LED_TX_INIT() do { LED_DDR |= _BV(1); } while (0)
#define LED_TX_ON() do { LED_PORT |= _BV(1); } while (0)
#define LED_TX_OFF() do { LED_PORT &= ~_BV(1); } while (0)
#define LED_RX_INIT() do { LED_DDR |= _BV(2); } while (0)
#define LED_RX_ON() do { LED_PORT |= _BV(2); } while (0)
#define LED_RX_OFF() do { LED_PORT &= ~_BV(2); } while (0)
void AFSK_init(Afsk *afsk);
void AFSK_transmit(char *buffer, size_t size);
void AFSK_poll(Afsk *afsk);
void afsk_putchar(char c);
int afsk_getchar(void);
#endif

Wyświetl plik

@ -0,0 +1,178 @@
// Based on work by Francesco Sacchi
#include "Arduino.h"
#include <string.h>
#include <ctype.h>
#include "AX25.h"
#include "HDLC.h"
#include "CRC-CCIT.h"
#include "AFSK.h"
#define countof(a) sizeof(a)/sizeof(a[0])
#define MIN(a,b) ({ typeof(a) _a = (a); typeof(b) _b = (b); ((typeof(_a))((_a < _b) ? _a : _b)); })
#define DECODE_CALL(buf, addr) for (unsigned i = 0; i < sizeof((addr))-CALL_OVERSPACE; i++) { char c = (*(buf)++ >> 1); (addr)[i] = (c == ' ') ? '\x0' : c; }
#define AX25_SET_REPEATED(msg, idx, val) do { if (val) { (msg)->rpt_flags |= _BV(idx); } else { (msg)->rpt_flags &= ~_BV(idx) ; } } while(0)
extern int LibAPRS_vref;
extern bool LibAPRS_open_squelch;
void ax25_init(AX25Ctx *ctx, ax25_callback_t hook) {
memset(ctx, 0, sizeof(*ctx));
ctx->hook = hook;
ctx->crc_in = ctx->crc_out = CRC_CCIT_INIT_VAL;
}
static void ax25_decode(AX25Ctx *ctx) {
AX25Msg msg;
uint8_t *buf = ctx->buf;
DECODE_CALL(buf, msg.dst.call);
msg.dst.ssid = (*buf++ >> 1) & 0x0F;
msg.dst.call[6] = 0;
DECODE_CALL(buf, msg.src.call);
msg.src.ssid = (*buf >> 1) & 0x0F;
msg.src.call[6] = 0;
for (msg.rpt_count = 0; !(*buf++ & 0x01) && (msg.rpt_count < countof(msg.rpt_list)); msg.rpt_count++) {
DECODE_CALL(buf, msg.rpt_list[msg.rpt_count].call);
//db1sb: terminate rpt_list.call-entries
msg.rpt_list[msg.rpt_count].call[6] = 0;
msg.rpt_list[msg.rpt_count].ssid = (*buf >> 1) & 0x0F;
AX25_SET_REPEATED(&msg, msg.rpt_count, (*buf & 0x80));
}
msg.ctrl = *buf++;
if (msg.ctrl != AX25_CTRL_UI) { return; }
msg.pid = *buf++;
if (msg.pid != AX25_PID_NOLAYER3) { return; }
msg.len = ctx->frame_len - 2 - (buf - ctx->buf);
msg.info = buf;
if (ctx->hook) {
cli();
ctx->hook(&msg);
sei();
}
}
void ax25_poll(AX25Ctx *ctx) {
int c;
while ((c = afsk_getchar()) != EOF) {
if (!ctx->escape && c == HDLC_FLAG) {
if (ctx->frame_len >= AX25_MIN_FRAME_LEN) {
if (ctx->crc_in == AX25_CRC_CORRECT) {
if(LibAPRS_open_squelch) {
LED_RX_ON();
}
ax25_decode(ctx);
}
}
ctx->sync = true;
ctx->crc_in = CRC_CCIT_INIT_VAL;
ctx->frame_len = 0;
continue;
}
if (!ctx->escape && c == HDLC_RESET) {
ctx->sync = false;
continue;
}
if (!ctx->escape && c == AX25_ESC) {
ctx->escape = true;
continue;
}
if (ctx->sync) {
if (ctx->frame_len < AX25_MAX_FRAME_LEN) {
ctx->buf[ctx->frame_len++] = c;
ctx->crc_in = update_crc_ccit(c, ctx->crc_in);
} else {
ctx->sync = false;
}
}
ctx->escape = false;
}
}
static void ax25_putchar(AX25Ctx *ctx, uint8_t c)
{
if (c == HDLC_FLAG || c == HDLC_RESET || c == AX25_ESC) afsk_putchar(AX25_ESC);
ctx->crc_out = update_crc_ccit(c, ctx->crc_out);
afsk_putchar(c);
}
void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len) {
ctx->crc_out = CRC_CCIT_INIT_VAL;
afsk_putchar(HDLC_FLAG);
const uint8_t *buf = (const uint8_t *)_buf;
while (len--) ax25_putchar(ctx, *buf++);
uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
ax25_putchar(ctx, crcl);
ax25_putchar(ctx, crch);
afsk_putchar(HDLC_FLAG);
}
static void ax25_sendCall(AX25Ctx *ctx, const AX25Call *addr, bool last){
unsigned len = MIN((sizeof(addr->call) - CALL_OVERSPACE), strlen(addr->call));
for (unsigned i = 0; i < len; i++) {
uint8_t c = addr->call[i];
c = toupper(c);
ax25_putchar(ctx, c << 1);
}
if (len < (sizeof(addr->call) - CALL_OVERSPACE)) {
for (unsigned i = 0; i < (sizeof(addr->call) - CALL_OVERSPACE) - len; i++) {
ax25_putchar(ctx, ' ' << 1);
}
}
//uint8_t ssid = 0x60 | (addr->ssid << 1) | (last ? 0x01 : 0);
//ax25_putchar(ctx, ssid);
if(strcmp(addr->call,"ARISS")==0){
ax25_putchar(ctx, ' ' << 1);
}else{
uint8_t ssid = 0x60 | (addr->ssid << 1) | (last ? 0x01 : 0);
ax25_putchar(ctx, ssid);
}
}
void ax25_sendVia(AX25Ctx *ctx, const AX25Call *path, size_t path_len, const void *_buf, size_t len) {
const uint8_t *buf = (const uint8_t *)_buf;
ctx->crc_out = CRC_CCIT_INIT_VAL;
afsk_putchar(HDLC_FLAG);
for (size_t i = 0; i < path_len; i++) {
ax25_sendCall(ctx, &path[i], (i == path_len - 1));
}
ax25_putchar(ctx, AX25_CTRL_UI);
ax25_putchar(ctx, AX25_PID_NOLAYER3);
while (len--) {
ax25_putchar(ctx, *buf++);
}
uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
ax25_putchar(ctx, crcl);
ax25_putchar(ctx, crch);
afsk_putchar(HDLC_FLAG);
}

Wyświetl plik

@ -0,0 +1,70 @@
#ifndef PROTOCOL_AX25_H
#define PROTOCOL_AX25_H
#include <stdio.h>
#include <stdbool.h>
#include "device.h"
#define AX25_MIN_FRAME_LEN 18
#ifndef CUSTOM_FRAME_SIZE
#define AX25_MAX_FRAME_LEN 330
#else
#define AX25_MAX_FRAME_LEN CUSTOM_FRAME_SIZE
#endif
#define AX25_CRC_CORRECT 0xF0B8
#define AX25_CTRL_UI 0x03
#define AX25_PID_NOLAYER3 0xF0
struct AX25Ctx; // Forward declarations
struct AX25Msg;
typedef void (*ax25_callback_t)(struct AX25Msg *msg);
typedef struct AX25Ctx {
uint8_t buf[AX25_MAX_FRAME_LEN];
FILE *ch;
size_t frame_len;
uint16_t crc_in;
uint16_t crc_out;
ax25_callback_t hook;
bool sync;
bool escape;
} AX25Ctx;
#define AX25_CALL(str, id) {.call = (str), .ssid = (id) }
#define AX25_MAX_RPT 8
#define AX25_REPEATED(msg, n) ((msg)->rpt_flags & BV(n))
#define CALL_OVERSPACE 1
typedef struct AX25Call {
char call[6+CALL_OVERSPACE];
//char STRING_TERMINATION = 0;
uint8_t ssid;
} AX25Call;
typedef struct AX25Msg {
AX25Call src;
AX25Call dst;
AX25Call rpt_list[AX25_MAX_RPT];
uint8_t rpt_count;
uint8_t rpt_flags;
uint16_t ctrl;
uint8_t pid;
const uint8_t *info;
size_t len;
} AX25Msg;
void ax25_sendVia(AX25Ctx *ctx, const AX25Call *path, size_t path_len, const void *_buf, size_t len);
#define ax25_send(ctx, dst, src, buf, len) ax25_sendVia(ctx, ({static AX25Call __path[]={dst, src}; __path;}), 2, buf, len)
void ax25_poll(AX25Ctx *ctx);
void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len);
void ax25_init(AX25Ctx *ctx, ax25_callback_t hook);
#endif

Wyświetl plik

@ -0,0 +1,36 @@
#include "CRC-CCIT.h"
const uint16_t crc_ccit_table[256] PROGMEM = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
};

Wyświetl plik

@ -0,0 +1,18 @@
// CRC-CCIT Implementation based on work by Francesco Sacchi
#ifndef CRC_CCIT_H
#define CRC_CCIT_H
#include <stdint.h>
#include <avr/pgmspace.h>
#define CRC_CCIT_INIT_VAL ((uint16_t)0xFFFF)
extern const uint16_t crc_ccit_table[256];
inline uint16_t update_crc_ccit(uint8_t c, uint16_t prev_crc) {
return (prev_crc >> 8) ^ pgm_read_word(&crc_ccit_table[(prev_crc ^ c) & 0xff]);
}
#endif

Wyświetl plik

@ -0,0 +1,85 @@
#ifndef UTIL_FIFO_H
#define UTIL_FIFO_H
#include <stddef.h>
#include <util/atomic.h>
typedef struct FIFOBuffer
{
unsigned char *begin;
unsigned char *end;
unsigned char * volatile head;
unsigned char * volatile tail;
} FIFOBuffer;
inline bool fifo_isempty(const FIFOBuffer *f) {
return f->head == f->tail;
}
inline bool fifo_isfull(const FIFOBuffer *f) {
return ((f->head == f->begin) && (f->tail == f->end)) || (f->tail == f->head - 1);
}
inline void fifo_push(FIFOBuffer *f, unsigned char c) {
*(f->tail) = c;
if (f->tail == f->end) {
f->tail = f->begin;
} else {
f->tail++;
}
}
inline unsigned char fifo_pop(FIFOBuffer *f) {
if(f->head == f->end) {
f->head = f->begin;
return *(f->end);
} else {
return *(f->head++);
}
}
inline void fifo_flush(FIFOBuffer *f) {
f->head = f->tail;
}
inline bool fifo_isempty_locked(const FIFOBuffer *f) {
bool result;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
result = fifo_isempty(f);
}
return result;
}
inline bool fifo_isfull_locked(const FIFOBuffer *f) {
bool result;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
result = fifo_isfull(f);
}
return result;
}
inline void fifo_push_locked(FIFOBuffer *f, unsigned char c) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
fifo_push(f, c);
}
}
inline unsigned char fifo_pop_locked(FIFOBuffer *f) {
unsigned char c;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
c = fifo_pop(f);
}
return c;
}
inline void fifo_init(FIFOBuffer *f, unsigned char *buffer, size_t size) {
f->head = f->tail = f->begin = buffer;
f->end = buffer + size -1;
}
inline size_t fifo_len(FIFOBuffer *f) {
return f->end - f->begin;
}
#endif

Wyświetl plik

@ -0,0 +1,8 @@
#ifndef PROTOCOL_HDLC_H
#define PROTOCOL_HDLC_H
#define HDLC_FLAG 0x7E
#define HDLC_RESET 0x7F
#define AX25_ESC 0x1B
#endif

Wyświetl plik

@ -0,0 +1,677 @@
(This project uses BertOS for some functionality. BertOS is licensed under GPLv2. Please see http://www.bertos.org for details. All files in the "bertos" directory originates from BertOS.)
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

Wyświetl plik

@ -0,0 +1,431 @@
#include "Arduino.h"
#include "AFSK.h"
#include "AX25.h"
Afsk modem;
AX25Ctx AX25;
extern void aprs_msg_callback(struct AX25Msg *msg);
#define countof(a) sizeof(a)/sizeof(a[0])
int LibAPRS_vref = REF_3V3;
bool LibAPRS_open_squelch = false;
unsigned long custom_preamble = 350UL;
unsigned long custom_tail = 50UL;
AX25Call src;
AX25Call dst;
AX25Call path1;
AX25Call path2;
char CALL[7] = "NOCALL";
int CALL_SSID = 0;
char DST[7] = "APZMDM";
int DST_SSID = 0;
char PATH1[7] = "WIDE1";
int PATH1_SSID = 1;
char PATH2[7] = "WIDE2";
int PATH2_SSID = 2;
int PATH_SIZE = 0;
AX25Call path[4];
// Location packet assembly fields
char latitude[9];
char longtitude[10];
char symbolTable = '/';
char symbol = 'n';
uint8_t power = 10;
uint8_t height = 10;
uint8_t gain = 10;
uint8_t directivity = 10;
/////////////////////////
// Message packet assembly fields
char message_recip[7];
int message_recip_ssid = -1;
int message_seq = 0;
char lastMessage[67];
size_t lastMessageLen;
bool message_autoAck = false;
/////////////////////////
void APRS_init(int reference, bool open_squelch) {
LibAPRS_vref = reference;
LibAPRS_open_squelch = open_squelch;
AFSK_init(&modem);
ax25_init(&AX25, aprs_msg_callback);
}
void APRS_poll(void) {
ax25_poll(&AX25);
}
void APRS_setCallsign(char *call, int ssid) {
memset(CALL, 0, 7);
int i = 0;
while (i < 6 && call[i] != 0) {
CALL[i] = call[i];
i++;
}
CALL_SSID = ssid;
}
void APRS_setDestination(char *call, int ssid) {
memset(DST, 0, 7);
int i = 0;
while (i < 6 && call[i] != 0) {
DST[i] = call[i];
i++;
}
DST_SSID = ssid;
}
void APRS_setPath1(char *call, int ssid) {
memset(PATH1, 0, 7);
int i = 0;
while (i < 6 && call[i] != 0) {
PATH1[i] = call[i];
i++;
}
PATH1_SSID = ssid;
}
void APRS_setPath2(char *call, int ssid) {
memset(PATH2, 0, 7);
int i = 0;
while (i < 6 && call[i] != 0) {
PATH2[i] = call[i];
i++;
}
PATH2_SSID = ssid;
}
void APRS_setPathSize(int pathSize) {
PATH_SIZE = pathSize;
}
void APRS_setMessageDestination(char *call, int ssid) {
memset(message_recip, 0, 7);
int i = 0;
while (i < 6 && call[i] != 0) {
message_recip[i] = call[i];
i++;
}
message_recip_ssid = ssid;
}
void APRS_setPreamble(unsigned long pre) {
custom_preamble = pre;
}
void APRS_setTail(unsigned long tail) {
custom_tail = tail;
}
void APRS_useAlternateSymbolTable(bool use) {
if (use) {
symbolTable = '\\';
} else {
symbolTable = '/';
}
}
void APRS_setSymbol(char sym) {
symbol = sym;
}
void APRS_setLat(char *lat) {
memset(latitude, 0, 9);
int i = 0;
while (i < 8 && lat[i] != 0) {
latitude[i] = lat[i];
i++;
}
}
void APRS_setLon(char *lon) {
memset(longtitude, 0, 10);
int i = 0;
while (i < 9 && lon[i] != 0) {
longtitude[i] = lon[i];
i++;
}
}
void APRS_setPower(int s) {
if (s >= 0 && s < 10) {
power = s;
}
}
void APRS_setHeight(int s) {
if (s >= 0 && s < 10) {
height = s;
}
}
void APRS_setGain(int s) {
if (s >= 0 && s < 10) {
gain = s;
}
}
void APRS_setDirectivity(int s) {
if (s >= 0 && s < 10) {
directivity = s;
}
}
void APRS_printSettings() {
Serial.println(F("LibAPRS Settings:"));
Serial.print(F("Callsign: ")); Serial.print(CALL); Serial.print(F("-")); Serial.println(CALL_SSID);
Serial.print(F("Destination: ")); Serial.print(DST); Serial.print(F("-")); Serial.println(DST_SSID);
Serial.print(F("Path1: ")); Serial.print(PATH1); Serial.print(F("-")); Serial.println(PATH1_SSID);
Serial.print(F("Path2: ")); Serial.print(PATH2); Serial.print(F("-")); Serial.println(PATH2_SSID);
Serial.print(F("Message dst: ")); if (message_recip[0] == 0) { Serial.println(F("N/A")); } else { Serial.print(message_recip); Serial.print(F("-")); Serial.println(message_recip_ssid); }
Serial.print(F("TX Preamble: ")); Serial.println(custom_preamble);
Serial.print(F("TX Tail: ")); Serial.println(custom_tail);
Serial.print(F("Symbol table: ")); if (symbolTable == '/') { Serial.println(F("Normal")); } else { Serial.println(F("Alternate")); }
Serial.print(F("Symbol: ")); Serial.println(symbol);
Serial.print(F("Power: ")); if (power < 10) { Serial.println(power); } else { Serial.println(F("N/A")); }
Serial.print(F("Height: ")); if (height < 10) { Serial.println(height); } else { Serial.println(F("N/A")); }
Serial.print(F("Gain: ")); if (gain < 10) { Serial.println(gain); } else { Serial.println(F("N/A")); }
Serial.print(F("Directivity: ")); if (directivity < 10) { Serial.println(directivity); } else { Serial.println(F("N/A")); }
Serial.print(F("Latitude: ")); if (latitude[0] != 0) { Serial.println(latitude); } else { Serial.println(F("N/A")); }
Serial.print(F("Longtitude: ")); if (longtitude[0] != 0) { Serial.println(longtitude); } else { Serial.println(F("N/A")); }
}
void APRS_sendPkt(void *_buffer, size_t length) {
uint8_t *buffer = (uint8_t *)_buffer;
memcpy(dst.call, DST, 6);
dst.ssid = DST_SSID;
memcpy(src.call, CALL, 6);
src.ssid = CALL_SSID;
memcpy(path1.call, PATH1, 6);
path1.ssid = PATH1_SSID;
memcpy(path2.call, PATH2, 6);
path2.ssid = PATH2_SSID;
path[0] = dst;
path[1] = src;
//modified for LibAPRS
//path[2] = path1;
//path[3] = path2;
size_t path_len = countof(path);
if(PATH_SIZE == 2){
path[2] = path1;
path[3] = path2;
} else if (PATH_SIZE == 1){
path[2] = path2;
path_len --;
} else if (PATH_SIZE == 0){
path_len -=2;
}
ax25_sendVia(&AX25, path, path_len, buffer, length);
//ax25_sendVia(&AX25, path, countof(path), buffer, length);
}
// Dynamic RAM usage of this function is 30 bytes
void APRS_sendLoc(void *_buffer, size_t length) {
size_t payloadLength = 20+length;
bool usePHG = false;
if (power < 10 && height < 10 && gain < 10 && directivity < 9) {
usePHG = true;
payloadLength += 7;
}
uint8_t *packet = (uint8_t*)malloc(payloadLength);
uint8_t *ptr = packet;
packet[0] = '=';
packet[9] = symbolTable;
packet[19] = symbol;
ptr++;
memcpy(ptr, latitude, 8);
ptr += 9;
memcpy(ptr, longtitude, 9);
ptr += 10;
if (usePHG) {
packet[20] = 'P';
packet[21] = 'H';
packet[22] = 'G';
packet[23] = power+48;
packet[24] = height+48;
packet[25] = gain+48;
packet[26] = directivity+48;
ptr+=7;
}
if (length > 0) {
uint8_t *buffer = (uint8_t *)_buffer;
memcpy(ptr, buffer, length);
}
APRS_sendPkt(packet, payloadLength);
free(packet);
}
//added for LibAPRS
void APRS_sendLocWtTmStmp(void *_buffer, size_t length, char *timestamp_buff) {
size_t payloadLength = 27+length;
bool usePHG = false;
if (power < 10 && height < 10 && gain < 10 && directivity < 9) {
usePHG = true;
payloadLength += 7;
}
uint8_t *packet = (uint8_t*)malloc(payloadLength);
uint8_t *ptr = packet;
packet[0] = '/';
packet[16] = symbolTable;
packet[26] = symbol;
ptr++;
memcpy(ptr, timestamp_buff, 7);
ptr += 7;
memcpy(ptr, latitude, 8);
ptr += 9;
memcpy(ptr, longtitude, 9);
ptr += 10;
if (usePHG) {
packet[27] = 'P';
packet[28] = 'H';
packet[29] = 'G';
packet[30] = power+48;
packet[31] = height+48;
packet[32] = gain+48;
packet[33] = directivity+48;
ptr+=7;
}
if (length > 0) {
uint8_t *buffer = (uint8_t *)_buffer;
memcpy(ptr, buffer, length);
}
APRS_sendPkt(packet, payloadLength);
free(packet);
}
void APRS_sendStatus(void *_buffer, size_t length) {
size_t payloadLength = 1+length;
uint8_t *packet = (uint8_t*)malloc(payloadLength);
uint8_t *ptr = packet;
packet[0] = '>';
ptr++;
if (length > 0) {
uint8_t *buffer = (uint8_t *)_buffer;
memcpy(ptr, buffer, length);
}
APRS_sendPkt(packet, payloadLength);
free(packet);
}
// Dynamic RAM usage of this function is 18 bytes
void APRS_sendMsg(void *_buffer, size_t length) {
if (length > 67) length = 67;
size_t payloadLength = 11+length+4;
uint8_t *packet = (uint8_t*)malloc(payloadLength);
uint8_t *ptr = packet;
packet[0] = ':';
int callSize = 6;
int count = 0;
while (callSize--) {
if (message_recip[count] != 0) {
packet[1+count] = message_recip[count];
count++;
}
}
if (message_recip_ssid != -1) {
packet[1+count] = '-'; count++;
if (message_recip_ssid < 10) {
packet[1+count] = message_recip_ssid+48; count++;
} else {
packet[1+count] = 49; count++;
packet[1+count] = message_recip_ssid-10+48; count++;
}
}
while (count < 9) {
packet[1+count] = ' '; count++;
}
packet[1+count] = ':';
ptr += 11;
if (length > 0) {
uint8_t *buffer = (uint8_t *)_buffer;
memcpy(ptr, buffer, length);
memcpy(lastMessage, buffer, length);
lastMessageLen = length;
}
message_seq++;
if (message_seq > 999) message_seq = 0;
packet[11+length] = '{';
int n = message_seq % 10;
int d = ((message_seq % 100) - n)/10;
int h = (message_seq - d - n) / 100;
packet[12+length] = h+48;
packet[13+length] = d+48;
packet[14+length] = n+48;
APRS_sendPkt(packet, payloadLength);
free(packet);
}
void APRS_msgRetry() {
message_seq--;
APRS_sendMsg(lastMessage, lastMessageLen);
}
// For getting free memory, from:
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15
extern unsigned int __heap_start;
extern void *__brkval;
struct __freelist {
size_t sz;
struct __freelist *nx;
};
extern struct __freelist *__flp;
int freeListSize() {
struct __freelist* current;
int total = 0;
for (current = __flp; current; current = current->nx) {
total += 2; /* Add two bytes for the memory block's header */
total += (int) current->sz;
}
return total;
}
int freeMemory() {
int free_memory;
if ((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__heap_start);
} else {
free_memory = ((int)&free_memory) - ((int)__brkval);
free_memory += freeListSize();
}
return free_memory;
}

Wyświetl plik

@ -0,0 +1,42 @@
#include "Arduino.h"
#include <stdint.h>
#include <stdbool.h>
#include "FIFO.h"
#include "CRC-CCIT.h"
#include "HDLC.h"
#include "AFSK.h"
#include "AX25.h"
void APRS_init(int reference, bool open_squelch);
void APRS_poll(void);
void APRS_setCallsign(char *call, int ssid);
void APRS_setDestination(char *call, int ssid);
void APRS_setMessageDestination(char *call, int ssid);
void APRS_setPath1(char *call, int ssid);
void APRS_setPath2(char *call, int ssid);
void APRS_setPathSize(int pathSize);
void APRS_setPreamble(unsigned long pre);
void APRS_setTail(unsigned long tail);
void APRS_useAlternateSymbolTable(bool use);
void APRS_setSymbol(char sym);
void APRS_setLat(char *lat);
void APRS_setLon(char *lon);
void APRS_setPower(int s);
void APRS_setHeight(int s);
void APRS_setGain(int s);
void APRS_setDirectivity(int s);
void APRS_sendPkt(void *_buffer, size_t length);
void APRS_sendLoc(void *_buffer, size_t length);
void APRS_sendLocWtTmStmp(void *_buffer, size_t length, char *timestamp_buff);
void APRS_sendStatus(void *_buffer, size_t length);
void APRS_sendMsg(void *_buffer, size_t length);
void APRS_msgRetry();
void APRS_printSettings();
int freeMemory();

Wyświetl plik

@ -0,0 +1,6 @@
#define m328p 0x01
#define m1284p 0x02
#define m644p 0x03
#define REF_3V3 0x01
#define REF_5V 0x02

Wyświetl plik

@ -0,0 +1,32 @@
#include "constants.h"
#ifndef DEVICE_CONFIGURATION
#define DEVICE_CONFIGURATION
// CPU settings
#ifndef TARGET_CPU
#define TARGET_CPU m328p
#endif
#ifndef F_CPU
#define F_CPU 16000000
#endif
#ifndef FREQUENCY_CORRECTION
#define FREQUENCY_CORRECTION 0
#endif
// Sampling & timer setup
#define CONFIG_AFSK_DAC_SAMPLERATE 9600
// Port settings
#if TARGET_CPU == m328p
#define DAC_PORT PORTD
#define DAC_DDR DDRD
#define LED_PORT PORTB
#define LED_DDR DDRB
#define ADC_PORT PORTC
#define ADC_DDR DDRC
#endif
#endif

Wyświetl plik

@ -0,0 +1,177 @@
// Include LibAPRS
#include <LibAPRS.h>
// You must define what reference voltage the ADC
// of your device is running at. If you bought a
// MicroModem from unsigned.io, it will be running
// at 3.3v if the "hw rev" is greater than 2.0.
// This is the most common. If you build your own
// modem, you should know this value yourself :)
#define ADC_REFERENCE REF_3V3
// OR
//#define ADC_REFERENCE REF_5V
// You can also define whether your modem will be
// running with an open squelch radio:
#define OPEN_SQUELCH false
// You always need to include this function. It will
// get called by the library every time a packet is
// received, so you can process incoming packets.
//
// If you are only interested in receiving, you should
// just leave this function empty.
//
// IMPORTANT! This function is called from within an
// interrupt. That means that you should only do things
// here that are FAST. Don't print out info directly
// from this function, instead set a flag and print it
// from your main loop, like this:
boolean gotPacket = false;
AX25Msg incomingPacket;
uint8_t *packetData;
void aprs_msg_callback(struct AX25Msg *msg) {
// If we already have a packet waiting to be
// processed, we must drop the new one.
if (!gotPacket) {
// Set flag to indicate we got a packet
gotPacket = true;
// The memory referenced as *msg is volatile
// and we need to copy all the data to a
// local variable for later processing.
memcpy(&incomingPacket, msg, sizeof(AX25Msg));
// We need to allocate a new buffer for the
// data payload of the packet. First we check
// if there is enough free RAM.
if (freeMemory() > msg->len) {
packetData = (uint8_t*)malloc(msg->len);
memcpy(packetData, msg->info, msg->len);
incomingPacket.info = packetData;
} else {
// We did not have enough free RAM to receive
// this packet, so we drop it.
gotPacket = false;
}
}
}
void setup() {
// Set up serial port
Serial.begin(115200);
// Initialise APRS library - This starts the modem
APRS_init(ADC_REFERENCE, OPEN_SQUELCH);
// You must at a minimum configure your callsign and SSID
APRS_setCallsign("NOCALL", 1);
// You don't need to set the destination identifier, but
// if you want to, this is how you do it:
// APRS_setDestination("APZMDM", 0);
// Path parameters are set to sensible values by
// default, but this is how you can configure them:
// APRS_setPath1("WIDE1", 1);
// APRS_setPath2("WIDE2", 2);
// You can define preamble and tail like this:
// APRS_setPreamble(350);
// APRS_setTail(50);
// You can use the normal or alternate symbol table:
// APRS_useAlternateSymbolTable(false);
// And set what symbol you want to use:
// APRS_setSymbol('n');
// We can print out all the settings
APRS_printSettings();
Serial.print(F("Free RAM: ")); Serial.println(freeMemory());
}
void locationUpdateExample() {
// Let's first set our latitude and longtitude.
// These should be in NMEA format!
APRS_setLat("5530.80N");
APRS_setLon("01143.89E");
// We can optionally set power/height/gain/directivity
// information. These functions accept ranges
// from 0 to 10, directivity 0 to 9.
// See this site for a calculator:
// http://www.aprsfl.net/phgr.php
// LibAPRS will only add PHG info if all four variables
// are defined!
APRS_setPower(2);
APRS_setHeight(4);
APRS_setGain(7);
APRS_setDirectivity(0);
// We'll define a comment string
char *comment = "LibAPRS location update";
// And send the update
APRS_sendLoc(comment, strlen(comment));
}
void messageExample() {
// We first need to set the message recipient
APRS_setMessageDestination("AA3BBB", 0);
// And define a string to send
char *message = "Hi there! This is a message.";
APRS_sendMsg(message, strlen(message));
}
// Here's a function to process incoming packets
// Remember to call this function often, so you
// won't miss any packets due to one already
// waiting to be processed
void processPacket() {
if (gotPacket) {
gotPacket = false;
Serial.print(F("Received APRS packet. SRC: "));
Serial.print(incomingPacket.src.call);
Serial.print(F("-"));
Serial.print(incomingPacket.src.ssid);
Serial.print(F(". DST: "));
Serial.print(incomingPacket.dst.call);
Serial.print(F("-"));
Serial.print(incomingPacket.dst.ssid);
Serial.print(F(". Data: "));
for (int i = 0; i < incomingPacket.len; i++) {
Serial.write(incomingPacket.info[i]);
}
Serial.println("");
// Remeber to free memory for our buffer!
free(packetData);
// You can print out the amount of free
// RAM to check you don't have any memory
// leaks
// Serial.print(F("Free RAM: ")); Serial.println(freeMemory());
}
}
boolean whichExample = false;
void loop() {
delay(1000);
if (whichExample) {
locationUpdateExample();
} else {
messageExample();
}
whichExample ^= true;
delay(500);
processPacket();
}

Wyświetl plik

@ -0,0 +1,36 @@
// **** INCLUDES *****
#include "LowPower.h"
void setup()
{
// No setup is required for this library
}
void loop()
{
// Enter idle state for 8 s with the rest of peripherals turned off
// Each microcontroller comes with different number of peripherals
// Comment off line of code where necessary
// ATmega328P, ATmega168
LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,
SPI_OFF, USART0_OFF, TWI_OFF);
// ATmega32U4
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF,
// TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
// ATmega2560
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF,
// TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART3_OFF,
// USART2_OFF, USART1_OFF, USART0_OFF, TWI_OFF);
// ATmega256RFR2
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF,
// TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF,
// USART1_OFF, USART0_OFF, TWI_OFF);
// Do something here
// Example: Read sensor, data logging, data transmission.
}

Wyświetl plik

@ -0,0 +1,33 @@
// **** INCLUDES *****
#include "LowPower.h"
// Use pin 2 as wake up pin
const int wakeUpPin = 2;
void wakeUp()
{
// Just a handler for the pin interrupt.
}
void setup()
{
// Configure wake up pin as input.
// This will consumes few uA of current.
pinMode(wakeUpPin, INPUT);
}
void loop()
{
// Allow wake up pin to trigger interrupt on low.
attachInterrupt(0, wakeUp, LOW);
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin is low.
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
// Disable external pin interrupt on wake up pin.
detachInterrupt(0);
// Do something here
// Example: Read sensor, data logging, data transmission.
}

Wyświetl plik

@ -0,0 +1,16 @@
// **** INCLUDES *****
#include "LowPower.h"
void setup()
{
// No setup is required for this library
}
void loop()
{
// Enter power down state for 8 s with ADC and BOD module disabled
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
// Do something here
// Example: Read sensor, data logging, data transmission.
}

Wyświetl plik

@ -0,0 +1,59 @@
// **** INCLUDES *****
#include "LowPower.h"
// External interrupt on pin 0 (use pin 0 to 24, except pin 4 on Arduino Zero)
const int pin = 0;
unsigned char count = 10;
void setup()
{
// Wait for serial USB port to open
while(!SerialUSB);
SerialUSB.println("***** ATSAMD21 Standby Mode Example *****");
// ***** IMPORTANT *****
// Delay is required to allow the USB interface to be active during
// sketch upload process
SerialUSB.println("Entering standby mode in:");
for (count; count > 0; count--)
{
SerialUSB.print(count);
SerialUSB.println(" s");
delay(1000);
}
// *********************
// External interrupt on pin (example: press of an active low button)
// A pullup resistor is used to hold the signal high when no button press
attachInterrupt(pin, blink, LOW);
}
void loop()
{
SerialUSB.println("Entering standby mode.");
SerialUSB.println("Apply low signal to wake the processor.");
SerialUSB.println("Zzzz...");
// Detach USB interface
USBDevice.detach();
// Enter standby mode
LowPower.standby();
// Attach USB interface
USBDevice.attach();
// Wait for serial USB port to open
while(!SerialUSB);
// Serial USB is blazing fast, you might miss the messages
delay(1000);
SerialUSB.println("Awake!");
SerialUSB.println("Send any character to enter standby mode again");
// Wait for user response
while(!SerialUSB.available());
while(SerialUSB.available() > 0)
{
SerialUSB.read();
}
}
void blink(void)
{
}

Wyświetl plik

@ -0,0 +1,173 @@
#ifndef LowPower_h
#define LowPower_h
#include "Arduino.h"
enum period_t
{
SLEEP_15MS,
SLEEP_30MS,
SLEEP_60MS,
SLEEP_120MS,
SLEEP_250MS,
SLEEP_500MS,
SLEEP_1S,
SLEEP_2S,
SLEEP_4S,
SLEEP_8S,
SLEEP_FOREVER
};
enum bod_t
{
BOD_OFF,
BOD_ON
};
enum adc_t
{
ADC_OFF,
ADC_ON
};
enum timer5_t
{
TIMER5_OFF,
TIMER5_ON
};
enum timer4_t
{
TIMER4_OFF,
TIMER4_ON
};
enum timer3_t
{
TIMER3_OFF,
TIMER3_ON
};
enum timer2_t
{
TIMER2_OFF,
TIMER2_ON
};
enum timer1_t
{
TIMER1_OFF,
TIMER1_ON
};
enum timer0_t
{
TIMER0_OFF,
TIMER0_ON
};
enum spi_t
{
SPI_OFF,
SPI_ON
};
enum usart0_t
{
USART0_OFF,
USART0_ON
};
enum usart1_t
{
USART1_OFF,
USART1_ON
};
enum usart2_t
{
USART2_OFF,
USART2_ON
};
enum usart3_t
{
USART3_OFF,
USART3_ON
};
enum twi_t
{
TWI_OFF,
TWI_ON
};
enum usb_t
{
USB_OFF,
USB_ON
};
enum idle_t
{
IDLE_0,
IDLE_1,
IDLE_2
};
class LowPowerClass
{
public:
#if defined (__AVR__)
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__)
void idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega644P__ || defined (__AVR_ATmega1284P__)
void idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart1_t usart1, usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega2560__
void idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart3_t usart3, usart2_t usart2, usart1_t usart1,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega256RFR2__
void idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart1_t usart1,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega32U4__
void idle(period_t period, adc_t adc, timer4_t timer4,
timer3_t timer3, timer1_t timer1, timer0_t timer0,
spi_t spi, usart1_t usart1, twi_t twi, usb_t usb);
#else
#error "Please ensure chosen MCU is either 168, 328P, 32U4, 2560 or 256RFR2."
#endif
void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2) __attribute__((optimize("-O1")));
void powerDown(period_t period, adc_t adc, bod_t bod) __attribute__((optimize("-O1")));
void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2) __attribute__((optimize("-O1")));
void powerStandby(period_t period, adc_t adc, bod_t bod) __attribute__((optimize("-O1")));
void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2) __attribute__((optimize("-O1")));
#elif defined (__arm__)
#if defined (__SAMD21G18A__)
void idle(idle_t idleMode);
void standby();
#else
#error "Please ensure chosen MCU is ATSAMD21G18A."
#endif
#else
#error "Processor architecture is not supported."
#endif
};
extern LowPowerClass LowPower;
#endif

Wyświetl plik

@ -0,0 +1,19 @@
### Low-Power
Lightweight low power library for Arduino.
Version: 1.70
Date: 04-06-2018
Devices Supported:
* ATMega168
* ATMega328P
* ATMega32U4
* ATMega644P
* ATMega1284P
* ATMega2560
* ATMega256RFR2
* ATSAMD21G18A
####Notes:
External interrupt during standby on ATSAMD21G18A requires a patch to the <a href="https://github.com/arduino/ArduinoCore-samd">Arduino SAMD Core</a> in order for it to work. Fix is provided by this particular <a href="https://github.com/arduino/ArduinoCore-samd/pull/90">pull request</a>.

Wyświetl plik

@ -0,0 +1,72 @@
#######################################
# Syntax Coloring Map LowPower
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
#######################################
# Methods and Functions (KEYWORD2)
#######################################
idle KEYWORD2
adcNoiseReduction KEYWORD2
powerDown KEYWORD2
powerSave KEYWORD2
powerStandby KEYWORD2
powerExtStandby KEYWORD2
standby KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
LowPower KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SLEEP_15MS LITERAL1
SLEEP_30MS LITERAL1
SLEEP_60MS LITERAL1
SLEEP_120MS LITERAL1
SLEEP_250MS LITERAL1
SLEEP_500MS LITERAL1
SLEEP_1S LITERAL1
SLEEP_2S LITERAL1
SLEEP_4S LITERAL1
SLEEP_8S LITERAL1
SLEEP_FOREVER LITERAL1
ADC_OFF LITERAL1
ADC_ON LITERAL1
BOD_OFF LITERAL1
BOD_ON LITERAL1
TIMER4_OFF LITERAL1
TIMER4_ON LITERAL1
TIMER3_OFF LITERAL1
TIMER3_ON LITERAL1
TIMER2_OFF LITERAL1
TIMER2_ON LITERAL1
TIMER1_OFF LITERAL1
TIMER1_ON LITERAL1
TIMER0_OFF LITERAL1
TIMER0_ON LITERAL1
USART3_OFF LITERAL1
USART3_ON LITERAL1
USART2_OFF LITERAL1
USART2_ON LITERAL1
USART1_OFF LITERAL1
USART1_ON LITERAL1
USART0_OFF LITERAL1
USART0_ON LITERAL1
SPI_OFF LITERAL1
SPI_ON LITERAL1
TWI_OFF LITERAL1
TWI_ON LITERAL1
USB_OFF LITERAL1
USB_ON LITERAL1
IDLE_0 LITERAL1
IDLE_1 LITERAL1
IDLE_2 LITERAL1

Wyświetl plik

@ -0,0 +1,9 @@
name=Low-Power
version=1.6
author=Rocket Scream Electronics
maintainer=Rocket Scream Electronics
sentence=Lightweight power management library
paragraph=Lightweight power management library
category=Other
url=https://github.com/rocketscream/Low-Power
architectures=avr,samd

Wyświetl plik

@ -0,0 +1,791 @@
Si5351 Library for Arduino
==========================
This is a library for the Si5351 series of clock generator ICs from [Silicon Labs](http://www.silabs.com) for the Arduino development environment. It will allow you to control the Si5351 with an Arduino, and without depending on the proprietary ClockBuilder software from Silicon Labs.
This library is focused towards usage in RF/amateur radio applications, but it may be useful in other cases. However, keep in mind that coding decisions are and will be made with those applications in mind first, so if you need something a bit different, please do fork this repository.
**Please feel free to use the Issues feature of GitHub if you run into problems or have suggestions for important features to implement. This is the best way to get in touch.**
Thanks For Your Support!
------------------------
If you would like to support my library development efforts, I would ask that you please consider purchasing a Si5351A Breakout Board from my [online store at etherkit.com](https://www.etherkit.com) and/or sending a [PayPal tip](https://paypal.me/NT7S). Thank you!
Library Installation
---------------------
The best way to install the library is via the Arduino Library Manager, which is available if you are using Arduino IDE version 1.6.2 or greater. To install it this way, simply go to the menu Sketch > Include Library > Manage Libraries..., and then in the search box at the upper-right, type "Etherkit Si5351". Click on the entry in the list below, then click on the provided "Install" button. By installing the library this way, you will always have notifications of future library updates, and can easily switch between library versions.
If you need to or would like to install the library in the old way, then you can download a copy of the library in a ZIP file. Download a ZIP file of the library from the GitHub repository by using the "Download ZIP" button at the right of the main repository page. Extract the ZIP file, then rename the unzipped folder as "Si5351". Finally, open the Arduino IDE, select menu Sketch > Import Library... > Add Library..., and select the renamed folder that you just downloaded. Restart the IDE and you should have access to the new library.
Hardware Requirements and Setup
-------------------------------
This library has been written for the Arduino platform and has been successfully tested on the Arduino Uno and an Uno clone. There should be no reason that it would not work on any other Arduino hardware with I2C support.
The Si5351 is a +3.3 V only part, so if you are not using a +3.3 V microcontroller, be sure you have some kind of level conversion strategy.
Wire the SDA and SCL pins of the Si5351 to the corresponding pins on the Arduino. Use the pin assignments posted on the [Arduino Wire library page](http://arduino.cc/en/Reference/Wire). Since the I2C interface is set to 100 kHz, use 1 to 10 k&Omega; pullup resistors from +3.3 V to the SDA and SCL lines.
Connect a 25 MHz or 27 MHz crystal with a load capacitance of 6, 8, or 10 pF to the Si5351 XA and XB pins. Locate the crystal as close to the Si5351 as possible and keep the traces as short as possible. Please use a SMT crystal. A crystal with leads will have too much stray capacitance.
Changes from v1 to v2
---------------------
The public interface to the v2 library is similar to the v1 library, but a few of the most-used methods have had their signatures changed, so your old programs won't compile right out-of-the-box after a library upgrade. Most importantly, the _init()_ and _set_freq()_ methods are different, so you'll at least need to change these calls in your old sketches.
The _init()_ method now has three parameters: the crystal load capacitance, the reference frequency, and the frequency correction value (with this last parameter being a new addition). You'll need to add that third parameter to your old _init()_ calls, but then you can delete any _set_correction()_ calls after that (unless you explicitly are changing the frequency correction after the initialization).
The _set_freq()_ method is now more streamlined and only requires two parameters: the desired output frequency (from 4 kHz to 225 MHz) and clock output. In your old code, you can delete the 2nd parameter in _set_freq()_, which was the PLL frequency. In case you want to do things manually, there is now a new method called _set_freq_manual()_ (see below for details).
Those two changes should cover nearly all upgrade scenarios, unless you were doing some lower-level use of the Si5351.
Example
-------
First, install the Si5351Arduino library into your instance of the Arduino IDE as described above.
There is a simple example named **si5351_example.ino** that is placed in your examples menu under the Si5351Arduino folder. Open this to see how to initialize the Si5351 and set a couple of the outputs to different frequencies. The commentary below will analyze the sample sketch.
Before you do anything with the Si5351, you will need to include the "si5351.h" and "Wire.h" header files and instantiate the Si5351 class.
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
Now in the _Setup()_ function, let's initialize communications with the Si5351, specify the load capacitance of the reference crystal, that we want to use the default reference oscillator frequency of 25 MHz (the second argument of "0" indicates that we want to use the default), and that we will apply no frequency correction at this point (the third argument of "0"):
i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
The _init()_ method returns a _bool_ which indicates whether the Arduino can communicate with a device on the I2C bus at the specified address (it does not verify that the device is an actual Si5351, but this is useful for ensuring that I2C communication is working).
Next, let's set the CLK0 output to 14 MHz:
si5351.set_freq(1400000000ULL, SI5351_CLK0);
Frequencies are indicated in units of 0.01 Hz. Therefore, if you prefer to work in 1 Hz increments in your own code, simply multiply each frequency passed to the library by 100ULL (better yet, use the define called _SI5351_FREQ_MULT_ in the header file for this multiplication).
In the main _Loop()_, we use the Serial port to monitor the status of the Si5351, using a method to update a public struct which holds the status bits:
si5351.update_status();
Serial.print("SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
When the synthesizers are locked and the Si5351 is working correctly, you'll see an output similar to this one (the REVID may be different):
SYS_INIT: 0 LOL_A: 0 LOL_B: 0 LOS: 0 REVID: 3
The nominal status for each of those flags is a 0. When the program indicates 1, there may be a reference clock problem, tuning problem, or some kind of other issue. (Note that it may take the Si5351 a bit of time to return the proper status flags, so in program initialization issue _update_status()_ and then give the Si5351 a few hundred milliseconds to initialize before querying the status flags again.)
A Brief Word about the Si5351 Architecture
------------------------------------------
The Si5351 consists of two main stages: two PLLs which are locked to the reference oscillator (a 25/27 MHz crystal) and which can be set from 600 to 900 MHz, and the output (multisynth) clocks which are locked to a PLL of choice and can be set from 500 kHz to 200 MHz (per the datasheet, although it does seem to be possible to set an output up to 225 MHz).
The B variant has an additional VCXO stage with control voltage pin which can be used as a reference synth for a clock output (PLLB must be used as the source for any VCXO output clock).
The C variant is able to take a reference clock input from 10 to 100 MHz separate from the standard crystal reference. If using this reference input, be sure to initialize the library with the correct frequency.
This library makes PLL assignments based on ease of use. They can be changed manually if needed, although that can introduce complications (see _Manually Selecting a PLL Frequency_ below).
Setting the Output Frequency
----------------------------
As indicated above, the library accepts and indicates clock and PLL frequencies in units of 0.01 Hz, as an _unsigned long long_ variable type (or _uint64_t_). When entering literal values, append ```ULL``` to make an explicit unsigned long long number to ensure proper tuning. Since many applications won't require sub-Hertz tuning, you may wish to use an _unsigned long_ (or _uint32_t_) variable to hold your tune frequency, then scale it up by multiplying by 100ULL before passing it to the _set_freq()_ method.
Using the _set_freq()_ method is the easiest way to use the library and gives you a wide range of tuning options, but has some constraints in its usage. Outputs CLK0 through CLK5 by default are all locked to PLLA while CLK6 and CLK7 are locked to PLLB. Due to the nature of the Si5351 architecture, there may only be one CLK output among those sharing a PLL which may be set greater than 100 MHz (actually specified at 112.5 MHz by SiLabs, but stability issues have been found at the upper end). Therefore, once one CLK output has been set above 100 MHz, no more CLKs on the same PLL will be allowed to be set greater than 100 MHz (unless the one which is already set is changed to a frequency below this threshold).
If the above constraints are not suitable, you need glitch-free tuning, or you are counting on multiple clocks being locked to the same reference, you may set the PLL frequency manually then make clock reference assignments to either of the PLLs.
Manually Selecting a PLL Frequency
----------------------------------
Instead of letting the library choose a PLL frequency for your chosen output frequency, you can choose it yourself in the _set_freq_manual()_ method. This method is similar to _set_freq()_, but the second argument is the desired PLL frequency:
si5351.set_freq_manual(19800000000ULL, 79200000000ULL, SI5351_CLK0);
**If you use this method (or the other methods to tweak the PLL and multisynth settings manually), it is very important to remember that the library will no longer properly track the PLL and multisynth settings and that you alone will be responsible for keeping the synths tuned properly. Strange things can happen to your other outputs if they are already in use. Be sure to read the Si5351 datasheet and Silicon Labs AN619 before doing this so that you understand what you are doing.**
When you are setting the PLL manually you need to be mindful of the limits of the IC. The multisynth (MS0 through MS5) is a fractional PLL, with limits described in AN619 as:
>Valid Multisynth divider ratios are 4, 6, 8, and any fractional value between 8 + 1/1,048,575 and 900 + 0/1.
This means that if any output is greater than 112.5 MHz (900 MHz/8), then this output frequency sets one
of the VCO frequencies.
To put this in other words, if you want to manually set the PLL and wish to have an output frequency greater than 100 MHz (changed in this library from the stated 112.5 MHz due to stability issues which were noticed), then the choice of PLL frequency is dictated by the choice of output frequency, and will need to be an even multiple of 4, 6, or 8.
Further Details
---------------
If we like we can adjust the output drive power:
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_4MA);
The drive strength is the amount of current into a 50&Omega; load. 2 mA roughly corresponds to 3 dBm output and 8 mA is approximately 10 dBm output.
Individual outputs can be turned on and off. In the second argument, use a 0 to disable and 1 to enable:
si5351.output_enable(SI5351_CLK0, 0);
You may invert a clock output signal by using this command:
si5351.set_clock_invert(SI5351_CLK0, 1);
Calibration
-----------
There will be some inherent error in the reference oscillator's actual frequency, so we can account for this by measuring the difference between the uncalibrated actual and nominal output frequencies, then using that difference as a correction factor in the library. The _init()_ and _set_correction()_ methods use a signed integer calibration constant measured in parts-per-billion. The easiest way to determine this correction factor is to measure a 10 MHz signal from one of the clock outputs (in Hz, or better resolution if you can measure it), scale it to parts-per-billion, then use it in the _set_correction()_ method in future use of this particular reference oscillator. Once this correction factor is determined, it should not need to be measured again for the same reference oscillator/Si5351 pair unless you want to redo the calibration. With an accurate measurement at one frequency, this calibration should be good across the entire tuning range.
The calibration method is called like this:
si5351.set_correction(-6190, SI5351_PLL_INPUT_XO);
However, you may use the third argument in the _init()_ method to specify the frequency correction and may not actually need to use the explict _set_correction()_ method in your code.
A handy calibration program is provided with the library in the example folder named _si5351_calibration_. To use it, simply hook up your Arduino to your Si5351, then connect it to a PC with the Arduino IDE. Connect the CLK0 output of the Si5351 to a frequency counter capable of measuring at 10 MHz (the more resolution, the better). Load the sketch then open the serial terminal window. Follow the prompts in the serial terminal to change the output frequency until your frequency counter reads exactly 10.000 000 00 MHz. The output from the Arduino on your serial terminal will tell you the correction factor you will need for future use of that reference oscillator/Si5351 combination.
One thing to note: the library is set for a 25 MHz reference crystal. If you are using a 27 MHz crystal, use the second parameter in the _init()_ method to specify that as the reference oscillator frequency.
Phase
------
_Please see the example sketch **si5351_phase.ino**_
The phase of the output clock signal can be changed by using the set_phase() method. Phase is in relation to (and measured against the period of) the PLL that the output multisynth is referencing. When you change the phase register from its default of 0, you will need to keep a few considerations in mind.
Setting the phase of a clock requires that you manually set the PLL and take the PLL frequency into account when calculation the value to place in the phase register. As shown on page 10 of Silicon Labs Application Note 619 (AN619), the phase register is a 7-bit register, where a bit represents a phase difference of 1/4 the PLL period. Therefore, the best way to get an accurate phase setting is to make the PLL an even multiple of the clock frequency, depending on what phase you need.
If you need a 90 degree phase shift (as in many RF applications), then it is quite easy to determine your parameters. Pick a PLL frequency that is an even multiple of your clock frequency (remember that the PLL needs to be in the range of 600 to 900 MHz). Then to set a 90 degree phase shift, you simply enter that multiple into the phase register. Remember when setting multiple outputs to be phase-related to each other, they each need to be referenced to the same PLL.
You can see this in action in a sketch in the examples folder called _si5351phase_. It shows how one would set up an I/Q pair of signals at 14.1 MHz.
// We will output 14.1 MHz on CLK0 and CLK1.
// A PLLA frequency of 705 MHz was chosen to give an even
// divisor by 14.1 MHz.
unsigned long long freq = 1410000000ULL;
unsigned long long pll_freq = 70500000000ULL;
// Set CLK0 and CLK1 to output 14.1 MHz with a fixed PLL frequency
si5351.set_freq_manual(freq, pll_freq, SI5351_CLK0);
si5351.set_freq_manual(freq, pll_freq, SI5351_CLK1);
// Now we can set CLK1 to have a 90 deg phase shift by entering
// 50 in the CLK1 phase register, since the ratio of the PLL to
// the clock frequency is 50.
si5351.set_phase(SI5351_CLK0, 0);
si5351.set_phase(SI5351_CLK1, 50);
// We need to reset the PLL before they will be in phase alignment
si5351.pll_reset(SI5351_PLLA);
CLK Output Options
------------------
_Please see the example sketch **si5351_outputs.ino**_
In most cases, you will most likely end up using the multisynth associated with a CLK output, but the Si5351 has some other options available as well. The reference clocks (both the crystal oscillator and the CLKIN signal) can be mirrored to any CLK output. Also CLK1 through CLK3 can mirror the MS0 (CLK0) output, and likewise the CLK5 through CLK7 outputs can mirror the MS4 (CLK4) output.
If you choose to use one or more of these output options, you first need to enable the fanout option for that particular signal:
// Enable clock fanout for the XO
si5351.set_clock_fanout(SI5351_FANOUT_XO, 1);
Once that is done, you can use the _set_clock_source()_ method to choose the output option you desire. Since the CLK outputs by default are turned off, you may need to turn your CLK output on as well:
// Set CLK1 to output the XO signal
si5351.set_clock_source(SI5351_CLK1, SI5351_CLK_SRC_XTAL);
si5351.output_enable(SI5351_CLK1, 1);
Using the VCXO (Si5351B)
-----------------------
_Please see the example sketch **si5351_vcxo.ino**_
The Si5351B variant has a VCXO feature which can be used to provide voltage-tunable clock outputs, with a voltage control input on pin 3 of the IC. This functionality is provided on the PLLB oscillator internal to the Si5351, so you must assign any clock outputs that you wish to voltage control to PLLB.
The library has a method named _set_vcxo()_ that allows you to set the PLLB frequency and the amount of pull range that you wish to use on that oscillator (from 30 to 240 parts-per-million). Using the VCXO is similar to manually setting an output frequency. First, call the _set_vcxo()_ method:
#define PLLB_FREQ 87600000000ULL
// Set VCXO osc to 876 MHz (146 MHz x 6), 40 ppm pull
si5351.set_vcxo(PLLB_FREQ, 40);
Next, we assign the desired VCXO clock output to PLLB:
// Set CLK0 to be locked to VCXO
si5351.set_ms_source(SI5351_CLK0, SI5351_PLLB);
Finally, we use the _set_freq_manual()_ method to set the clock output center frequency:
// Tune to 146 MHz center frequency
si5351.set_freq_manual(14600000000ULL, PLLB_FREQ, SI5351_CLK0);
Using an External Reference (Si5351C)
-------------------------------------
_Please see the example sketch **si5351_ext_ref.ino**_
The Si5351C variant has a CLKIN input (pin 6) which allows the use of an alternate external CMOS clock reference from 10 to 100 MHz. Either PLLA and/or PLLB can be locked to this external reference. The library tracks the referenced frequencies and correction factors individually for both the crystal oscillator reference (XO) and external reference (CLKIN).
The XO reference frequency is set during the call to _init()_. If you are going to use the external reference clock, then set its nominal frequency with the _set_ref_freq()_ method:
// Set the CLKIN reference frequency to 10 MHz
si5351.set_ref_freq(10000000UL, SI5351_PLL_INPUT_CLKIN);
A correction factor for the external reference clock may also now be set:
// Apply a correction factor to CLKIN
si5351.set_correction(0, SI5351_PLL_INPUT_CLKIN);
The _set_pll_input()_ method is used to set the desired PLLs to reference to the external reference signal on CLKIN instead of the XO signal:
// Set PLLA and PLLB to use the signal on CLKIN instead of the XTAL
si5351.set_pll_input(SI5351_PLLA, SI5351_PLL_INPUT_CLKIN);
si5351.set_pll_input(SI5351_PLLB, SI5351_PLL_INPUT_CLKIN);
Once that is set, the library can be used as you normally would, with all of the frequency calculations done based on the reference frequency set in _set_ref_freq()_.
Alternate I2C Addresses
-----------------------
The standard I2C bus address for the Si5351 is 0x60, however there are other ICs in the wild that use alternate bus addresses. In order to accommodate these ICs, the class constructor can be called with the I2C bus address as a parameter, as shown in this example:
Si5351 si5351(0x61);
Startup Conditions
------------------
This library initializes the Si5351 parameters to the following values upon startup and on reset:
Multisynths 0 through 5 (and hence the matching clock outputs CLK0 through CLK5) are assigned to PLLA, while Multisynths 6 and 7 are assigned to PLLB.
PLLA and PLLB are set to 800 MHz (also defined as _SI5351_PLL_FIXED_ in the library).
All CLK outputs are set to 0 Hz and disabled.
Default drive strength is 2 mA on each output.
Constraints
-----------
* Two multisynths cannot share a PLL with when both outputs are >= 100 MHz. The library will refuse to set another multisynth to a frequency in that range if another multisynth sharing the same PLL is already within that frequency range.
* Setting phase will be limited in the extreme edges of the output tuning ranges. Because the phase register is 7-bits in size and is denominated in units representing 1/4 the PLL period, not all phases can be set for all output frequencies. For example, if you need a 90&deg; phase shift, the lowest frequency you can set it at is 4.6875 MHz (600 MHz PLL/128).
* The frequency range of Multisynth 6 and 7 is ~18.45 kHz to 150 MHz. The library assigns PLLB to these two multisynths, so if you choose to use both, then both frequencies must be an even divisor of the PLL frequency (between 6 and 254), so plan accordingly. You can see the current PLLB frequency by accessing the _pllb_freq_ public member.
* VCXO pull range can be &plusmn;30 to &plusmn;240 ppm
Public Methods
--------------
### init()
```
/*
* init(uint8_t xtal_load_c, uint32_t ref_osc_freq, int32_t corr)
*
* Setup communications to the Si5351 and set the crystal
* load capacitance.
*
* xtal_load_c - Crystal load capacitance. Use the SI5351_CRYSTAL_LOAD_*PF
* defines in the header file
* xo_freq - Crystal/reference oscillator frequency in 1 Hz increments.
* Defaults to 25000000 if a 0 is used here.
* corr - Frequency correction constant in parts-per-billion
*
* Returns a boolean that indicates whether a device was found on the desired
* I2C address.
*
*/
bool Si5351::init(uint8_t xtal_load_c, uint32_t ref_osc_freq, uint32_t ref_osc_freq)
```
### reset()
```
/*
* reset(void)
*
* Call to reset the Si5351 to the state initialized by the library.
*
*/
void Si5351::reset(void)
```
### set_freq()
```
/*
* set_freq(uint64_t freq, enum si5351_clock clk)
*
* Sets the clock frequency of the specified CLK output
*
* freq - Output frequency in Hz
* clk - Clock output
* (use the si5351_clock enum)
*/
uint8_t Si5351::set_freq(uint64_t freq, enum si5351_clock clk)
```
### set_freq_manual()
```
/*
* set_freq_manual(uint64_t freq, uint64_t pll_freq, enum si5351_clock clk)
*
* Sets the clock frequency of the specified CLK output using the given PLL
* frequency. You must ensure that the MS is assigned to the correct PLL and
* that the PLL is set to the correct frequency before using this method.
*
* It is important to note that if you use this method, you will have to
* track that all settings are sane yourself.
*
* freq - Output frequency in Hz
* pll_freq - Frequency of the PLL driving the Multisynth in Hz * 100
* clk - Clock output
* (use the si5351_clock enum)
*/
```
### set_pll()
```
/*
* set_pll(uint64_t pll_freq, enum si5351_pll target_pll)
*
* Set the specified PLL to a specific oscillation frequency
*
* pll_freq - Desired PLL frequency in Hz * 100
* target_pll - Which PLL to set
* (use the si5351_pll enum)
*/
void Si5351::set_pll(uint64_t pll_freq, enum si5351_pll target_pll)
```
### set_ms()
```
/*
* set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4)
*
* Set the specified multisynth parameters. Not normally needed, but public for advanced users.
*
* clk - Clock output
* (use the si5351_clock enum)
* int_mode - Set integer mode
* Set to 1 to enable, 0 to disable
* r_div - Desired r_div ratio
* div_by_4 - Set Divide By 4 mode
* Set to 1 to enable, 0 to disable
*/
void Si5351::set_ms(enum si5351_clock clk, struct Si5351RegSet ms_reg, uint8_t int_mode, uint8_t r_div, uint8_t div_by_4)
```
### output_enable()
```
/*
* output_enable(enum si5351_clock clk, uint8_t enable)
*
* Enable or disable a chosen output
* clk - Clock output
* (use the si5351_clock enum)
* enable - Set to 1 to enable, 0 to disable
*/
void Si5351::output_enable(enum si5351_clock clk, uint8_t enable)
```
### drive_strength()
```
/*
* drive_strength(enum si5351_clock clk, enum si5351_drive drive)
*
* Sets the drive strength of the specified clock output
*
* clk - Clock output
* (use the si5351_clock enum)
* drive - Desired drive level
* (use the si5351_drive enum)
*/
void Si5351::drive_strength(enum si5351_clock clk, enum si5351_drive drive)
```
### update_status()
```
/*
* update_status(void)
*
* Call this to update the status structs, then access them
* via the dev_status and dev_int_status global variables.
*
* See the header file for the struct definitions. These
* correspond to the flag names for registers 0 and 1 in
* the Si5351 datasheet.
*/
void Si5351::update_status(void)
```
### set_correction()
```
/*
* set_correction(int32_t corr, enum si5351_pll_input ref_osc)
*
* corr - Correction factor in ppb
* ref_osc - Desired reference oscillator
* (use the si5351_pll_input enum)
*
* Use this to set the oscillator correction factor.
* This value is a signed 32-bit integer of the
* parts-per-billion value that the actual oscillation
* frequency deviates from the specified frequency.
*
* The frequency calibration is done as a one-time procedure.
* Any desired test frequency within the normal range of the
* Si5351 should be set, then the actual output frequency
* should be measured as accurately as possible. The
* difference between the measured and specified frequencies
* should be calculated in Hertz, then multiplied by 10 in
* order to get the parts-per-billion value.
*
* Since the Si5351 itself has an intrinsic 0 PPM error, this
* correction factor is good across the entire tuning range of
* the Si5351. Once this calibration is done accurately, it
* should not have to be done again for the same Si5351 and
* crystal.
*/
void Si5351::set_correction(int32_t corr, enum si5351_pll_input ref_osc)
```
### set_phase()
```
/*
* set_phase(enum si5351_clock clk, uint8_t phase)
*
* clk - Clock output
* (use the si5351_clock enum)
* phase - 7-bit phase word
* (in units of VCO/4 period)
*
* Write the 7-bit phase register. This must be used
* with a user-set PLL frequency so that the user can
* calculate the proper tuning word based on the PLL period.
*/
void Si5351::set_phase(enum si5351_clock clk, uint8_t phase)
```
### get_correction()
```
/*
* get_correction(enum si5351_pll_input ref_osc)
*
* ref_osc - Desired reference oscillator
* 0: crystal oscillator (XO)
* 1: external clock input (CLKIN)
*
* Returns the oscillator correction factor stored
* in RAM.
*/
int32_t Si5351::get_correction(enum si5351_pll_input ref_osc)
```
### pll_reset()
```
/*
* pll_reset(enum si5351_pll target_pll)
*
* target_pll - Which PLL to reset
* (use the si5351_pll enum)
*
* Apply a reset to the indicated PLL.
*/
void Si5351::pll_reset(enum si5351_pll target_pll)
```
### set_ms_source()
```
/*
* set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
*
* clk - Clock output
* (use the si5351_clock enum)
* pll - Which PLL to use as the source
* (use the si5351_pll enum)
*
* Set the desired PLL source for a multisynth.
*/
void Si5351::set_ms_source(enum si5351_clock clk, enum si5351_pll pll)
```
### set_int()
```
/*
* set_int(enum si5351_clock clk, uint8_t int_mode)
*
* clk - Clock output
* (use the si5351_clock enum)
* enable - Set to 1 to enable, 0 to disable
*
* Set the indicated multisynth into integer mode.
*/
void Si5351::set_int(enum si5351_clock clk, uint8_t enable)
```
### set_clock_pwr()
```
/*
* set_clock_pwr(enum si5351_clock clk, uint8_t pwr)
*
* clk - Clock output
* (use the si5351_clock enum)
* pwr - Set to 1 to enable, 0 to disable
*
* Enable or disable power to a clock output (a power
* saving feature).
*/
void Si5351::set_clock_pwr(enum si5351_clock clk, uint8_t pwr)
```
### set_clock_invert()
```
/*
* set_clock_invert(enum si5351_clock clk, uint8_t inv)
*
* clk - Clock output
* (use the si5351_clock enum)
* inv - Set to 1 to enable, 0 to disable
*
* Enable to invert the clock output waveform.
*/
void Si5351::set_clock_invert(enum si5351_clock clk, uint8_t inv)
```
### set_clock_source()
```
/*
* set_clock_source(enum si5351_clock clk, enum si5351_clock_source src)
*
* clk - Clock output
* (use the si5351_clock enum)
* src - Which clock source to use for the multisynth
* (use the si5351_clock_source enum)
*
* Set the clock source for a multisynth (based on the options
* presented for Registers 16-23 in the Silicon Labs AN619 document).
* Choices are XTAL, CLKIN, MS0, or the multisynth associated with
* the clock output.
*/
void Si5351::set_clock_source(enum si5351_clock clk, enum si5351_clock_source src)
```
### set_clock_disable()
```
/*
* set_clock_disable(enum si5351_clock clk, enum si5351_clock_disable dis_state)
*
* clk - Clock output
* (use the si5351_clock enum)
* dis_state - Desired state of the output upon disable
* (use the si5351_clock_disable enum)
*
* Set the state of the clock output when it is disabled. Per page 27
* of AN619 (Registers 24 and 25), there are four possible values: low,
* high, high impedance, and never disabled.
*/
void Si5351::set_clock_disable(enum si5351_clock clk, enum si5351_clock_disable dis_state)
```
### set_clock_fanout()
```
/*
* set_clock_fanout(enum si5351_clock_fanout fanout, uint8_t enable)
*
* fanout - Desired clock fanout
* (use the si5351_clock_fanout enum)
* enable - Set to 1 to enable, 0 to disable
*
* Use this function to enable or disable the clock fanout options
* for individual clock outputs. If you intend to output the XO or
* CLKIN on the clock outputs, enable this first.
*
* By default, only the Multisynth fanout is enabled at startup.
*/
void Si5351::set_clock_fanout(enum si5351_clock_fanout fanout, uint8_t enable)
```
### set_pll_input()
```
/*
* set_pll_input(enum si5351_pll pll, enum si5351_pll_input input)
*
* pll - Which PLL to use as the source
* (use the si5351_pll enum)
* input - Which reference oscillator to use as PLL input
* (use the si5351_pll_input enum)
*
* Set the desired reference oscillator source for the given PLL.
*/
void Si5351::set_pll_input(enum si5351_pll pll, enum si5351_pll_input input)
```
### set_vcxo()
```
/*
* set_vcxo(uint64_t pll_freq, uint8_t ppm)
*
* pll_freq - Desired PLL base frequency in Hz * 100
* ppm - VCXO pull limit in ppm
*
* Set the parameters for the VCXO on the Si5351B.
*/
void Si5351::set_vcxo(uint64_t pll_freq, uint8_t ppm)
```
### set_ref_freq()
```
/*
* set_ref_freq(uint32_t ref_freq, enum si5351_pll_input ref_osc)
*
* ref_freq - Reference oscillator frequency in Hz
* ref_osc - Which reference oscillator frequency to set
* (use the si5351_pll_input enum)
*
* Set the reference frequency value for the desired reference oscillator
*/
void Si5351::set_ref_freq(uint32_t ref_freq, enum si5351_pll_input ref_osc)
```
### si5351_write_bulk()
```
uint8_t Si5351::si5351_write_bulk(uint8_t addr, uint8_t bytes, uint8_t *data)
```
### si5351_write()
```
uint8_t Si5351::si5351_write(uint8_t addr, uint8_t data)
```
### si5351_read()
```
uint8_t Si5351::si5351_read(uint8_t addr)
```
Public Variables
----------------
struct Si5351Status dev_status;
struct Si5351IntStatus dev_int_status;
enum si5351_pll pll_assignment[8];
uint64_t clk_freq[8];
uint64_t plla_freq;
uint64_t pllb_freq;
uint32_t xtal_freq;
Tokens
------
Here are the defines, structs, and enumerations you will find handy to use with the library.
Crystal load capacitance:
SI5351_CRYSTAL_LOAD_0PF
SI5351_CRYSTAL_LOAD_6PF
SI5351_CRYSTAL_LOAD_8PF
SI5351_CRYSTAL_LOAD_10PF
Clock outputs:
enum si5351_clock {SI5351_CLK0, SI5351_CLK1, SI5351_CLK2, SI5351_CLK3,
SI5351_CLK4, SI5351_CLK5, SI5351_CLK6, SI5351_CLK7};
PLL sources:
enum si5351_pll {SI5351_PLLA, SI5351_PLLB};
Drive levels:
enum si5351_drive {SI5351_DRIVE_2MA, SI5351_DRIVE_4MA, SI5351_DRIVE_6MA, SI5351_DRIVE_8MA};
Clock sources:
enum si5351_clock_source {SI5351_CLK_SRC_XTAL, SI5351_CLK_SRC_CLKIN, SI5351_CLK_SRC_MS0, SI5351_CLK_SRC_MS};
Clock disable states:
enum si5351_clock_disable {SI5351_CLK_DISABLE_LOW, SI5351_CLK_DISABLE_HIGH, SI5351_CLK_DISABLE_HI_Z, SI5351_CLK_DISABLE_NEVER};
Clock fanout:
enum si5351_clock_fanout {SI5351_FANOUT_CLKIN, SI5351_FANOUT_XO, SI5351_FANOUT_MS};
PLL input sources:
enum si5351_pll_input{SI5351_PLL_INPUT_XO, SI5351_PLL_INPUT_CLKIN};
Status register:
struct Si5351Status
{
uint8_t SYS_INIT;
uint8_t LOL_B;
uint8_t LOL_A;
uint8_t LOS;
uint8_t REVID;
};
Interrupt register:
struct Si5351IntStatus
{
uint8_t SYS_INIT_STKY;
uint8_t LOL_B_STKY;
uint8_t LOL_A_STKY;
uint8_t LOS_STKY;
};
Raw Commands
------------
If you need to read and write raw data to the Si5351, there is public access to the library's _read()_, _write()_, and _write_bulk()_ methods.
Unsupported Features
--------------------
This library does not currently support the spread spectrum function of the Si5351.
Changelog
---------
* v2.1.3
* Correct error in si5351_example.ino sketch
* v2.1.2
* Correct error in si5351_calibration.ino sketch
* v2.1.1
* Add bool return value to _init()_ indicating whether a device is on the I2C bus
* v2.1.0
* Add support for reference frequencies and corrections for both the XO and CLKIN
* v2.0.7
* Change _set_freq()_ behavior so that the output is only automatically enabled the very first time that _set_freq()_ is called
* v2.0.6
* Call _set_pll()_ in _set_correction()_ to ensure that the new correction factor is applied
* v2.0.5
* Remove PLL reset from _set_freq()_ when not necessary
* v2.0.4
* Fix error in VCXO algorithm
* v2.0.3
* Fix regression in _set_freq()_ that wiped out proper R div setting, causing errors in setting low frequency outputs
* v2.0.2
* Increase maximum frequency in _set_freq()_ to 225 MHz
* Change SI5351_MULTISYNTH_SHARE_MAX from 112.5 MHz to 100 MHz due to stability issues
* Add explicit reset of VCXO param in _reset()_
* Add I2C bus address parameter and default to class constructor
* Update si5351_calibration example sketch
* v2.0.1
* Fix logic error in _set_freq()_ which causes errors in setting multiple clocks >100 MHz
* v2.0.0
* Complete rewrite of tuning algorithm
* Add support for setting CLK6 and CLK7
* Add support for VCXO (on Si5351B)
* Change interface of _init()_ and _set_freq()_
* Add _set_freq_manual()_ method
* Add _reset()_ method
* Added many new example sketches
* v1.1.2
* Fix error where register 183 is not pre-loaded with correct value per AN619. Add define for SI5351_CRYSTAL_LOAD_0PF (undocumented in AN619 but present in the official ClockBuilder software).
* v1.1.1
* Fix if statement eval error in _set_clock_disable()_
* v1.1.0
* Added _set_pll_input()_ method to allow toggling the PLL reference source for the Si5351C variant and added support to _init()_ for different PLL reference frequencies from 10 to 100 MHz.
* v1.0.0
* Initial release
[1]: http://www.silabs.com
[2]: https://www.etherkit.com

Wyświetl plik

@ -0,0 +1,125 @@
/*
* si5351_calibration.ino - Simple calibration routine for the Si5351
* breakout board.
*
* Copyright 2015 - 2018 Paul Warren <pwarren@pwarren.id.au>
* Jason Milldrum <milldrum@gmail.com>
*
* Uses code from https://github.com/darksidelemm/open_radio_miniconf_2015
* and the old version of the calibration sketch
*
* This sketch 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.
* Foobar 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.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
int32_t cal_factor = 0;
int32_t old_cal = 0;
uint64_t rx_freq;
uint64_t target_freq = 1000000000ULL; // 10 MHz, in hundredths of hertz
void setup()
{
// Start serial and initialize the Si5351
Serial.begin(57600);
// The crystal load value needs to match in order to have an accurate calibration
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Start on target frequency
si5351.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(target_freq, SI5351_CLK0);
}
void loop()
{
si5351.update_status();
if (si5351.dev_status.SYS_INIT == 1)
{
Serial.println(F("Initialising Si5351, you shouldn't see many of these!"));
delay(500);
}
else
{
Serial.println();
Serial.println(F("Adjust until your frequency counter reads as close to 10 MHz as possible."));
Serial.println(F("Press 'q' when complete."));
vfo_interface();
}
}
static void flush_input(void)
{
while (Serial.available() > 0)
Serial.read();
}
static void vfo_interface(void)
{
rx_freq = target_freq;
cal_factor = old_cal;
Serial.println(F(" Up: r t y u i o p"));
Serial.println(F(" Down: f g h j k l ;"));
Serial.println(F(" Hz: 0.01 0.1 1 10 100 1K 10k"));
while (1)
{
if (Serial.available() > 0)
{
char c = Serial.read();
switch (c)
{
case 'q':
flush_input();
Serial.println();
Serial.print(F("Calibration factor is "));
Serial.println(cal_factor);
Serial.println(F("Setting calibration factor"));
si5351.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
Serial.println(F("Resetting target frequency"));
si5351.set_freq(target_freq, SI5351_CLK0);
old_cal = cal_factor;
return;
case 'r': rx_freq += 1; break;
case 'f': rx_freq -= 1; break;
case 't': rx_freq += 10; break;
case 'g': rx_freq -= 10; break;
case 'y': rx_freq += 100; break;
case 'h': rx_freq -= 100; break;
case 'u': rx_freq += 1000; break;
case 'j': rx_freq -= 1000; break;
case 'i': rx_freq += 10000; break;
case 'k': rx_freq -= 10000; break;
case 'o': rx_freq += 100000; break;
case 'l': rx_freq -= 100000; break;
case 'p': rx_freq += 1000000; break;
case ';': rx_freq -= 1000000; break;
default:
// Do nothing
continue;
}
cal_factor = (int32_t)(target_freq - rx_freq) + old_cal;
si5351.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.pll_reset(SI5351_PLLA);
si5351.set_freq(target_freq, SI5351_CLK0);
Serial.print(F("Current difference:"));
Serial.println(cal_factor);
}
}
}

Wyświetl plik

@ -0,0 +1,68 @@
/*
* si5351_clk67_example.ino - Simple example of setting CLK6 and CLK7
* outputs using Si5351Arduino library
*
* Copyright (C) 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
void setup()
{
// Start serial and initialize the Si5351
Serial.begin(57600);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Set CLK0 to output 14 MHz
si5351.set_freq(1400000000ULL, SI5351_CLK0);
// Set CLK6 to 23 MHz, library sets PLL to 874 MHz
si5351.set_freq(2300000000ULL, SI5351_CLK6);
// This won't work since it is not an even multiple of the PLL (874 MHz)
si5351.set_freq(1234567800ULL, SI5351_CLK7);
// Setting CLK7 to 87.4 MHz will work
si5351.set_freq(8740000000ULL, SI5351_CLK7);
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print("PLLA: ");
Serial.print((uint32_t)(si5351.plla_freq/100));
Serial.print(" PLLB: ");
Serial.print((uint32_t)(si5351.pllb_freq/100));
Serial.print(" SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,66 @@
/*
* si5351_example.ino - Simple example of using Si5351Arduino library
*
* Copyright (C) 2015 - 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
void setup()
{
bool i2c_found;
// Start serial and initialize the Si5351
Serial.begin(57600);
i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
if(!i2c_found)
{
Serial.println("Device not found on I2C bus!");
}
// Set CLK0 to output 14 MHz
si5351.set_freq(1400000000ULL, SI5351_CLK0);
// Set CLK1 to output 175 MHz
si5351.set_ms_source(SI5351_CLK1, SI5351_PLLB);
si5351.set_freq_manual(17500000000ULL, 70000000000ULL, SI5351_CLK1);
// Query a status update and wait a bit to let the Si5351 populate the
// status flags correctly.
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print("SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,67 @@
/*
* si5351_ext_ref.ino - Simple example of using an external reference
* clock with the Si5351Arduino library
*
* Copyright (C) 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
void setup()
{
// Start serial
Serial.begin(57600);
// Initialize the Si5351 to use a 25 MHz clock on the XO input
si5351.init(SI5351_CRYSTAL_LOAD_0PF, 0, 0);
// Set the CLKIN reference frequency to 10 MHz
si5351.set_ref_freq(10000000UL, SI5351_PLL_INPUT_CLKIN);
// Apply a correction factor to CLKIN
si5351.set_correction(0, SI5351_PLL_INPUT_CLKIN);
// Set PLLA and PLLB to use the signal on CLKIN instead of the XTAL
si5351.set_pll_input(SI5351_PLLA, SI5351_PLL_INPUT_CLKIN);
si5351.set_pll_input(SI5351_PLLB, SI5351_PLL_INPUT_CLKIN);
// Set CLK0 to output 14 MHz
si5351.set_freq(1400000000ULL, SI5351_CLK0);
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print(" SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,72 @@
/*
* si5351_outputs.ino - How to set different output sources
* with the Si5351Arduino library
*
* Copyright (C) 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
void setup()
{
// Start serial and initialize the Si5351
Serial.begin(57600);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Set CLK0 to output 14 MHz
si5351.set_freq(1400000000ULL, SI5351_CLK0);
// Enable clock fanout for the XO
si5351.set_clock_fanout(SI5351_FANOUT_XO, 1);
// Enable clock fanout for MS
si5351.set_clock_fanout(SI5351_FANOUT_MS, 1);
// Set CLK1 to output the XO signal
si5351.set_clock_source(SI5351_CLK1, SI5351_CLK_SRC_XTAL);
si5351.output_enable(SI5351_CLK1, 1);
// Set CLK2 to mirror the MS0 (CLK0) output
si5351.set_clock_source(SI5351_CLK2, SI5351_CLK_SRC_MS0);
si5351.output_enable(SI5351_CLK2, 1);
// Change CLK0 output to 10 MHz, observe how CLK2 also changes
si5351.set_freq(1000000000ULL, SI5351_CLK0);
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print(" SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,96 @@
/*
* si5351_phase.ino - Example for setting phase with Si5351Arduino library
*
* Copyright (C) 2015 - 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Setting the phase of a clock requires that you manually set the PLL and
* take the PLL frequency into account when calculation the value to place
* in the phase register. As shown on page 10 of Silicon Labs Application
* Note 619 (AN619), the phase register is a 7-bit register, where a bit
* represents a phase difference of 1/4 the PLL period. Therefore, the best
* way to get an accurate phase setting is to make the PLL an even multiple
* of the clock frequency, depending on what phase you need.
*
* If you need a 90 degree phase shift (as in many RF applications), then
* it is quite easy to determine your parameters. Pick a PLL frequency that
* is an even multiple of your clock frequency (remember that the PLL needs
* to be in the range of 600 to 900 MHz). Then to set a 90 degree phase shift,
* you simply enter that multiple into the phase register. Remember when
* setting multiple outputs to be phase-related to each other, they each need
* to be referenced to the same PLL.
*/
#include "si5351.h"
#include "Wire.h"
Si5351 si5351;
void setup()
{
// Start serial and initialize the Si5351
Serial.begin(57600);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// We will output 14.1 MHz on CLK0 and CLK1.
// A PLLA frequency of 705 MHz was chosen to give an even
// divisor by 14.1 MHz.
unsigned long long freq = 1410000000ULL;
unsigned long long pll_freq = 70500000000ULL;
// Set CLK0 and CLK1 to use PLLA as the MS source.
// This is not explicitly necessary in v2 of this library,
// as these are already the default assignments.
// si5351.set_ms_source(SI5351_CLK0, SI5351_PLLA);
// si5351.set_ms_source(SI5351_CLK1, SI5351_PLLA);
// Set CLK0 and CLK1 to output 14.1 MHz with a fixed PLL frequency
si5351.set_freq_manual(freq, pll_freq, SI5351_CLK0);
si5351.set_freq_manual(freq, pll_freq, SI5351_CLK1);
// Now we can set CLK1 to have a 90 deg phase shift by entering
// 50 in the CLK1 phase register, since the ratio of the PLL to
// the clock frequency is 50.
si5351.set_phase(SI5351_CLK0, 0);
si5351.set_phase(SI5351_CLK1, 50);
// We need to reset the PLL before they will be in phase alignment
si5351.pll_reset(SI5351_PLLA);
// Query a status update and wait a bit to let the Si5351 populate the
// status flags correctly.
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print("SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,176 @@
/*
* si5351_sweeper.ino - Si5351 Simple Sweep Generator
*
* Copyright (c) 2016 Thomas S. Knutsen <la3pna@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
*WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*
* Connect Si5351 to I2C
* Sweep out is on pin 5, ranging from 0-5V (3.3V).
* Use a filter on sweep out voltage. 100K + 1uF should be a good start.
* A op-amp can be used to improve the filtering of the DC voltage.
*/
int correction = 0; // use the Si5351 correction sketch to find the frequency correction factor
int inData = 0;
long steps = 100;
unsigned long startFreq = 10000000;
unsigned long stopFreq = 100000000;
int analogpin = 5;
int delaytime = 50;
#include <si5351.h>
#include "Wire.h"
Si5351 si5351;
void setup()
{
Serial.begin(57600);
/*
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
*/
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, correction);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_6MA);
info();
}
void info()
{
Serial.println("Si5351 Sweeper");
Serial.println("A = Start frequency");
Serial.println("B = Stop frequency");
Serial.println("S = Stepsize");
Serial.println("M = Single sweep");
Serial.println("C = Continious sweep until Q");
Serial.print("T = Timestep in ms, currently ");
Serial.println(delaytime);
}
void loop()
{
inData = 0;
if(Serial.available() > 0) // see if incoming serial data:
{
inData = Serial.read(); // read oldest byte in serial buffer:
}
if(inData == 'M' || inData == 'm')
{
inData = 0;
unsigned long freqstep = (stopFreq - startFreq) / steps;
for(int i = 0; i < (steps + 1); i++ )
{
unsigned long freq = startFreq + (freqstep*i);
si5351.set_freq(freq * SI5351_FREQ_MULT, SI5351_CLK0);
analogWrite(analogpin, map(i, 0, steps, 0, 255));
delay(delaytime);
}
si5351.output_enable(SI5351_CLK0, 0);
}
if(inData == 'C' || inData == 'c')
{
boolean running = true;
inData = 0;
while(running)
{
unsigned long freqstep = (stopFreq - startFreq) / steps;
for (int i = 0; i < (steps + 1); i++ )
{
unsigned long freq = startFreq + (freqstep * i);
si5351.set_freq(freq * SI5351_FREQ_MULT, SI5351_CLK0);
analogWrite(analogpin, map(i, 0, steps, 0, 255));
delay(delaytime);
if(Serial.available() > 0) // see if incoming serial data:
{
inData = Serial.read(); // read oldest byte in serial buffer:
if(inData == 'Q' || inData == 'q')
{
running = false;
inData = 0;
}
}
}
}
si5351.output_enable(SI5351_CLK0, 0);
}
if(inData == 'S' || inData == 's')
{
steps = Serial.parseInt();
Serial.print("Steps: ");
Serial.println(steps);
inData = 0;
}
if(inData == 'H' || inData == 'h')
{
info();
}
if(inData == 'T' || inData == 't')
{
delaytime = Serial.parseInt();
Serial.print("time pr step: ");
Serial.println(delaytime);
inData = 0;
}
if(inData == 'L' || inData == 'l')
{
for (int i = 0; i < (steps+1); i++ )
{
// print out the value you read:
Serial.print(i * 10);
Serial.print(';');
Serial.print(steps);
Serial.print(';');
Serial.println(-i);
delay(10); // delay in between reads for stability
}
inData = 0;
}
if(inData == 'A' || inData == 'a')
{
startFreq = Serial.parseInt();
Serial.print("Start: ");
Serial.println(startFreq);
inData = 0;
}
if(inData == 'B' || inData == 'b')
{
stopFreq = Serial.parseInt();
Serial.print("Stop: ");
Serial.println(stopFreq);
inData = 0;
}
}

Wyświetl plik

@ -0,0 +1,65 @@
/*
* si5351_vcxo.ino - Example for using the Si5351B VCXO functions
* with Si5351Arduino library
*
* Copyright (C) 2016 Jason Milldrum <milldrum@gmail.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "si5351.h"
#include "Wire.h"
#define PLLB_FREQ 87600000000ULL
Si5351 si5351;
void setup()
{
// Start serial and initialize the Si5351
Serial.begin(57600);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
// Set VCXO osc to 876 MHz (146 MHz x 6), 40 ppm pull
si5351.set_vcxo(PLLB_FREQ, 40);
// Set CLK0 to be locked to VCXO
si5351.set_ms_source(SI5351_CLK0, SI5351_PLLB);
// Tune to 146 MHz center frequency
si5351.set_freq_manual(14600000000ULL, PLLB_FREQ, SI5351_CLK0);
si5351.update_status();
delay(500);
}
void loop()
{
// Read the Status Register and print it every 10 seconds
si5351.update_status();
Serial.print("corr: ");
Serial.print(si5351.get_correction());
Serial.print(" SYS_INIT: ");
Serial.print(si5351.dev_status.SYS_INIT);
Serial.print(" LOL_A: ");
Serial.print(si5351.dev_status.LOL_A);
Serial.print(" LOL_B: ");
Serial.print(si5351.dev_status.LOL_B);
Serial.print(" LOS: ");
Serial.print(si5351.dev_status.LOS);
Serial.print(" REVID: ");
Serial.println(si5351.dev_status.REVID);
delay(10000);
}

Wyświetl plik

@ -0,0 +1,81 @@
Si5351 KEYWORD1
init KEYWORD2
reset KEYWORD2
set_freq KEYWORD2
set_freq_manual KEYWORD2
set_pll KEYWORD2
set_ms KEYWORD2
output_enable KEYWORD2
drive_strength KEYWORD2
update_status KEYWORD2
set_correction KEYWORD2
set_phase KEYWORD2
get_correction KEYWORD2
pll_reset KEYWORD2
set_ms_source KEYWORD2
set_int KEYWORD2
set_clock_pwr KEYWORD2
set_clock_invert KEYWORD2
set_clock_source KEYWORD2
set_clock_disable KEYWORD2
set_clock_fanout KEYWORD2
set_pll_input KEYWORD2
set_vcxo KEYWORD2
set_ref_freq KEYWORD2
si5351_write_bulk KEYWORD2
si5351_write KEYWORD2
si5351_read KEYWORD2
dev_status KEYWORD2
dev_int_status KEYWORD2
pll_assignment KEYWORD2
clk_freq KEYWORD2
plla_freq KEYWORD2
pllb_freq KEYWORD2
xtal_freq KEYWORD2
plla_ref_osc KEYWORD2
pllb_ref_osc KEYWORD2
SI5351_PLL_FIXED LITERAL1
SI5351_FREQ_MULT LITERAL1
SI5351_DEFAULT_CLK LITERAL1
SI5351_CRYSTAL_LOAD_0PF LITERAL1
SI5351_CRYSTAL_LOAD_6PF LITERAL1
SI5351_CRYSTAL_LOAD_8PF LITERAL1
SI5351_CRYSTAL_LOAD_10PF LITERAL1
SI5351_CLK0 LITERAL1
SI5351_CLK1 LITERAL1
SI5351_CLK2 LITERAL1
SI5351_CLK3 LITERAL1
SI5351_CLK4 LITERAL1
SI5351_CLK5 LITERAL1
SI5351_CLK6 LITERAL1
SI5351_CLK7 LITERAL1
SI5351_PLLA LITERAL1
SI5351_PLLB LITERAL1
SI5351_DRIVE_2MA LITERAL1
SI5351_DRIVE_4MA LITERAL1
SI5351_DRIVE_6MA LITERAL1
SI5351_DRIVE_8MA LITERAL1
SI5351_CLK_SRC_XTAL LITERAL1
SI5351_CLK_SRC_CLKIN LITERAL1
SI5351_CLK_SRC_MS0 LITERAL1
SI5351_CLK_SRC_MS LITERAL1
SI5351_CLK_DISABLE_LOW LITERAL1
SI5351_CLK_DISABLE_HIGH LITERAL1
SI5351_CLK_DISABLE_HI_Z LITERAL1
SI5351_CLK_DISABLE_NEVER LITERAL1
SI5351_FANOUT_CLKIN LITERAL1
SI5351_FANOUT_XO LITERAL1
SI5351_FANOUT_MS LITERAL1
SI5351_PLL_INPUT_XO LITERAL1
SI5351_PLL_INPUT_CLKIN LITERAL1
SYS_INIT LITERAL1
LOL_B LITERAL1
LOL_A LITERAL1
LOS LITERAL1
REVID LITERAL1
SYS_INIT_STKY LITERAL1
LOL_B_STKY LITERAL1
LOL_A_STKY LITERAL1
LOS_STKY LITERAL1

Wyświetl plik

@ -0,0 +1,9 @@
name=Etherkit Si5351
version=2.1.3
author=Jason Milldrum <milldrum@gmail.com>
maintainer=Jason Milldrum <milldrum@gmail.com>
sentence=A full-featured library for the Si5351 series of clock generator ICs from Silicon Labs
paragraph=This library will allow you to control nearly all features of the Si5351, without depending on the proprietary ClockBuilder software from Silicon Labs.
category=Device Control
url=https://github.com/etherkit/Si5351Arduino
architectures=*

Wyświetl plik

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

Wyświetl plik

@ -0,0 +1,335 @@
/*
* si5351.h - Si5351 library for Arduino
*
* Copyright (C) 2015 - 2016 Jason Milldrum <milldrum@gmail.com>
* Dana H. Myers <k6jq@comcast.net>
*
* Many defines derived from clk-si5351.h in the Linux kernel.
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Rabeeh Khoury <rabeeh@solid-run.com>
*
* do_div() macro derived from /include/asm-generic/div64.h in
* the Linux kernel.
* Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SI5351_H_
#define SI5351_H_
#include "Arduino.h"
#include "Wire.h"
#include <stdint.h>
/* Define definitions */
#define SI5351_BUS_BASE_ADDR 0x60
#define SI5351_XTAL_FREQ 25000000
#define SI5351_PLL_FIXED 80000000000ULL
#define SI5351_FREQ_MULT 100ULL
#define SI5351_DEFAULT_CLK 1000000000ULL
#define SI5351_PLL_VCO_MIN 600000000
#define SI5351_PLL_VCO_MAX 900000000
#define SI5351_MULTISYNTH_MIN_FREQ 500000
#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000
#define SI5351_MULTISYNTH_MAX_FREQ 225000000
#define SI5351_MULTISYNTH_SHARE_MAX 100000000
#define SI5351_MULTISYNTH_SHARE_MIN 1024000
#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ
#define SI5351_CLKOUT_MIN_FREQ 4000
#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ
#define SI5351_CLKOUT67_MS_MIN SI5351_PLL_VCO_MIN / SI5351_MULTISYNTH67_A_MAX
#define SI5351_CLKOUT67_MIN_FREQ SI5351_CLKOUT67_MS_MIN / 128
#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ
#define SI5351_PLL_A_MIN 15
#define SI5351_PLL_A_MAX 90
#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1)
#define SI5351_PLL_C_MAX 1048575
#define SI5351_MULTISYNTH_A_MIN 6
#define SI5351_MULTISYNTH_A_MAX 1800
#define SI5351_MULTISYNTH67_A_MAX 254
#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1)
#define SI5351_MULTISYNTH_C_MAX 1048575
#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1)
#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1)
#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1)
#define SI5351_VCXO_PULL_MIN 30
#define SI5351_VCXO_PULL_MAX 240
#define SI5351_VCXO_MARGIN 103
#define SI5351_DEVICE_STATUS 0
#define SI5351_INTERRUPT_STATUS 1
#define SI5351_INTERRUPT_MASK 2
#define SI5351_STATUS_SYS_INIT (1<<7)
#define SI5351_STATUS_LOL_B (1<<6)
#define SI5351_STATUS_LOL_A (1<<5)
#define SI5351_STATUS_LOS (1<<4)
#define SI5351_OUTPUT_ENABLE_CTRL 3
#define SI5351_OEB_PIN_ENABLE_CTRL 9
#define SI5351_PLL_INPUT_SOURCE 15
#define SI5351_CLKIN_DIV_MASK (3<<6)
#define SI5351_CLKIN_DIV_1 (0<<6)
#define SI5351_CLKIN_DIV_2 (1<<6)
#define SI5351_CLKIN_DIV_4 (2<<6)
#define SI5351_CLKIN_DIV_8 (3<<6)
#define SI5351_PLLB_SOURCE (1<<3)
#define SI5351_PLLA_SOURCE (1<<2)
#define SI5351_CLK0_CTRL 16
#define SI5351_CLK1_CTRL 17
#define SI5351_CLK2_CTRL 18
#define SI5351_CLK3_CTRL 19
#define SI5351_CLK4_CTRL 20
#define SI5351_CLK5_CTRL 21
#define SI5351_CLK6_CTRL 22
#define SI5351_CLK7_CTRL 23
#define SI5351_CLK_POWERDOWN (1<<7)
#define SI5351_CLK_INTEGER_MODE (1<<6)
#define SI5351_CLK_PLL_SELECT (1<<5)
#define SI5351_CLK_INVERT (1<<4)
#define SI5351_CLK_INPUT_MASK (3<<2)
#define SI5351_CLK_INPUT_XTAL (0<<2)
#define SI5351_CLK_INPUT_CLKIN (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
#define SI5351_CLK3_0_DISABLE_STATE 24
#define SI5351_CLK7_4_DISABLE_STATE 25
#define SI5351_CLK_DISABLE_STATE_MASK 3
#define SI5351_CLK_DISABLE_STATE_LOW 0
#define SI5351_CLK_DISABLE_STATE_HIGH 1
#define SI5351_CLK_DISABLE_STATE_FLOAT 2
#define SI5351_CLK_DISABLE_STATE_NEVER 3
#define SI5351_PARAMETERS_LENGTH 8
#define SI5351_PLLA_PARAMETERS 26
#define SI5351_PLLB_PARAMETERS 34
#define SI5351_CLK0_PARAMETERS 42
#define SI5351_CLK1_PARAMETERS 50
#define SI5351_CLK2_PARAMETERS 58
#define SI5351_CLK3_PARAMETERS 66
#define SI5351_CLK4_PARAMETERS 74
#define SI5351_CLK5_PARAMETERS 82
#define SI5351_CLK6_PARAMETERS 90
#define SI5351_CLK7_PARAMETERS 91
#define SI5351_CLK6_7_OUTPUT_DIVIDER 92
#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4)
#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0)
#define SI5351_OUTPUT_CLK_DIV_SHIFT 4
#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0
#define SI5351_OUTPUT_CLK_DIV_1 0
#define SI5351_OUTPUT_CLK_DIV_2 1
#define SI5351_OUTPUT_CLK_DIV_4 2
#define SI5351_OUTPUT_CLK_DIV_8 3
#define SI5351_OUTPUT_CLK_DIV_16 4
#define SI5351_OUTPUT_CLK_DIV_32 5
#define SI5351_OUTPUT_CLK_DIV_64 6
#define SI5351_OUTPUT_CLK_DIV_128 7
#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2)
#define SI5351_SSC_PARAM0 149
#define SI5351_SSC_PARAM1 150
#define SI5351_SSC_PARAM2 151
#define SI5351_SSC_PARAM3 152
#define SI5351_SSC_PARAM4 153
#define SI5351_SSC_PARAM5 154
#define SI5351_SSC_PARAM6 155
#define SI5351_SSC_PARAM7 156
#define SI5351_SSC_PARAM8 157
#define SI5351_SSC_PARAM9 158
#define SI5351_SSC_PARAM10 159
#define SI5351_SSC_PARAM11 160
#define SI5351_SSC_PARAM12 161
#define SI5351_VXCO_PARAMETERS_LOW 162
#define SI5351_VXCO_PARAMETERS_MID 163
#define SI5351_VXCO_PARAMETERS_HIGH 164
#define SI5351_CLK0_PHASE_OFFSET 165
#define SI5351_CLK1_PHASE_OFFSET 166
#define SI5351_CLK2_PHASE_OFFSET 167
#define SI5351_CLK3_PHASE_OFFSET 168
#define SI5351_CLK4_PHASE_OFFSET 169
#define SI5351_CLK5_PHASE_OFFSET 170
#define SI5351_PLL_RESET 177
#define SI5351_PLL_RESET_B (1<<7)
#define SI5351_PLL_RESET_A (1<<5)
#define SI5351_CRYSTAL_LOAD 183
#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
#define SI5351_CRYSTAL_LOAD_0PF (0<<6)
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
#define SI5351_FANOUT_ENABLE 187
#define SI5351_CLKIN_ENABLE (1<<7)
#define SI5351_XTAL_ENABLE (1<<6)
#define SI5351_MULTISYNTH_ENABLE (1<<4)
/* Macro definitions */
//#define RFRAC_DENOM ((1L << 20) - 1)
#define RFRAC_DENOM 1000000ULL
/*
* Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
*
* The semantics of do_div() are:
*
* uint32_t do_div(uint64_t *n, uint32_t base)
* {
* uint32_t remainder = *n % base;
* *n = *n / base;
* return remainder;
* }
*
* NOTE: macro parameter n is evaluated multiple times,
* beware of side effects!
*/
# define do_div(n,base) ({ \
uint64_t __base = (base); \
uint64_t __rem; \
__rem = ((uint64_t)(n)) % __base; \
(n) = ((uint64_t)(n)) / __base; \
__rem; \
})
/* Enum definitions */
/*
* enum si5351_variant - SiLabs Si5351 chip variant
* @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
* @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
* @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
* @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
*/
/*
enum si5351_variant {
SI5351_VARIANT_A = 1,
SI5351_VARIANT_A3 = 2,
SI5351_VARIANT_B = 3,
SI5351_VARIANT_C = 4,
};
*/
enum si5351_clock {SI5351_CLK0, SI5351_CLK1, SI5351_CLK2, SI5351_CLK3,
SI5351_CLK4, SI5351_CLK5, SI5351_CLK6, SI5351_CLK7};
enum si5351_pll {SI5351_PLLA, SI5351_PLLB};
enum si5351_drive {SI5351_DRIVE_2MA, SI5351_DRIVE_4MA, SI5351_DRIVE_6MA, SI5351_DRIVE_8MA};
enum si5351_clock_source {SI5351_CLK_SRC_XTAL, SI5351_CLK_SRC_CLKIN, SI5351_CLK_SRC_MS0, SI5351_CLK_SRC_MS};
enum si5351_clock_disable {SI5351_CLK_DISABLE_LOW, SI5351_CLK_DISABLE_HIGH, SI5351_CLK_DISABLE_HI_Z, SI5351_CLK_DISABLE_NEVER};
enum si5351_clock_fanout {SI5351_FANOUT_CLKIN, SI5351_FANOUT_XO, SI5351_FANOUT_MS};
enum si5351_pll_input {SI5351_PLL_INPUT_XO, SI5351_PLL_INPUT_CLKIN};
/* Struct definitions */
struct Si5351RegSet
{
uint32_t p1;
uint32_t p2;
uint32_t p3;
};
struct Si5351Status
{
uint8_t SYS_INIT;
uint8_t LOL_B;
uint8_t LOL_A;
uint8_t LOS;
uint8_t REVID;
};
struct Si5351IntStatus
{
uint8_t SYS_INIT_STKY;
uint8_t LOL_B_STKY;
uint8_t LOL_A_STKY;
uint8_t LOS_STKY;
};
class Si5351
{
public:
Si5351(uint8_t i2c_addr = SI5351_BUS_BASE_ADDR);
bool init(uint8_t, uint32_t, int32_t);
void reset(void);
uint8_t set_freq(uint64_t, enum si5351_clock);
uint8_t set_freq_manual(uint64_t, uint64_t, enum si5351_clock);
void set_pll(uint64_t, enum si5351_pll);
void set_ms(enum si5351_clock, struct Si5351RegSet, uint8_t, uint8_t, uint8_t);
void output_enable(enum si5351_clock, uint8_t);
void drive_strength(enum si5351_clock, enum si5351_drive);
void update_status(void);
void set_correction(int32_t, enum si5351_pll_input);
void set_phase(enum si5351_clock, uint8_t);
int32_t get_correction(enum si5351_pll_input);
void pll_reset(enum si5351_pll);
void set_ms_source(enum si5351_clock, enum si5351_pll);
void set_int(enum si5351_clock, uint8_t);
void set_clock_pwr(enum si5351_clock, uint8_t);
void set_clock_invert(enum si5351_clock, uint8_t);
void set_clock_source(enum si5351_clock, enum si5351_clock_source);
void set_clock_disable(enum si5351_clock, enum si5351_clock_disable);
void set_clock_fanout(enum si5351_clock_fanout, uint8_t);
void set_pll_input(enum si5351_pll, enum si5351_pll_input);
void set_vcxo(uint64_t, uint8_t);
void set_ref_freq(uint32_t, enum si5351_pll_input);
uint8_t si5351_write_bulk(uint8_t, uint8_t, uint8_t *);
uint8_t si5351_write(uint8_t, uint8_t);
uint8_t si5351_read(uint8_t);
struct Si5351Status dev_status = {.SYS_INIT = 0, .LOL_B = 0, .LOL_A = 0,
.LOS = 0, .REVID = 0};
struct Si5351IntStatus dev_int_status = {.SYS_INIT_STKY = 0, .LOL_B_STKY = 0,
.LOL_A_STKY = 0, .LOS_STKY = 0};
enum si5351_pll pll_assignment[8];
uint64_t clk_freq[8];
uint64_t plla_freq;
uint64_t pllb_freq;
enum si5351_pll_input plla_ref_osc;
enum si5351_pll_input pllb_ref_osc;
uint32_t xtal_freq[2];
private:
uint64_t pll_calc(enum si5351_pll, uint64_t, struct Si5351RegSet *, int32_t, uint8_t);
uint64_t multisynth_calc(uint64_t, uint64_t, struct Si5351RegSet *);
uint64_t multisynth67_calc(uint64_t, uint64_t, struct Si5351RegSet *);
void update_sys_status(struct Si5351Status *);
void update_int_status(struct Si5351IntStatus *);
void ms_div(enum si5351_clock, uint8_t, uint8_t);
uint8_t select_r_div(uint64_t *);
uint8_t select_r_div_ms67(uint64_t *);
int32_t ref_correction[2];
uint8_t clkin_div;
uint8_t i2c_bus_addr;
bool clk_first_set[8];
};
#endif /* SI5351_H_ */

Wyświetl plik

@ -0,0 +1,97 @@
/* DateStrings.cpp
* Definitions for date strings for use with the Time library
*
* Updated for Arduino 1.5.7 18 July 2014
*
* No memory is consumed in the sketch if your code does not call any of the string methods
* You can change the text of the strings, make sure the short strings are each exactly 3 characters
* the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h
*
*/
#if defined(__AVR__)
#include <avr/pgmspace.h>
#else
// for compatiblity with Arduino Due and Teensy 3.0 and maybe others?
#define PROGMEM
#define PGM_P const char *
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define pgm_read_word(addr) (*(const unsigned char **)(addr))
#define strcpy_P(dest, src) strcpy((dest), (src))
#endif
#include <string.h> // for strcpy_P or strcpy
#include "TimeLib.h"
// the short strings for each day or month must be exactly dt_SHORT_STR_LEN
#define dt_SHORT_STR_LEN 3 // the length of short strings
static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null
const char monthStr0[] PROGMEM = "";
const char monthStr1[] PROGMEM = "January";
const char monthStr2[] PROGMEM = "February";
const char monthStr3[] PROGMEM = "March";
const char monthStr4[] PROGMEM = "April";
const char monthStr5[] PROGMEM = "May";
const char monthStr6[] PROGMEM = "June";
const char monthStr7[] PROGMEM = "July";
const char monthStr8[] PROGMEM = "August";
const char monthStr9[] PROGMEM = "September";
const char monthStr10[] PROGMEM = "October";
const char monthStr11[] PROGMEM = "November";
const char monthStr12[] PROGMEM = "December";
const PROGMEM char * const PROGMEM monthNames_P[] =
{
monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6,
monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12
};
const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec";
const char dayStr0[] PROGMEM = "Err";
const char dayStr1[] PROGMEM = "Sunday";
const char dayStr2[] PROGMEM = "Monday";
const char dayStr3[] PROGMEM = "Tuesday";
const char dayStr4[] PROGMEM = "Wednesday";
const char dayStr5[] PROGMEM = "Thursday";
const char dayStr6[] PROGMEM = "Friday";
const char dayStr7[] PROGMEM = "Saturday";
const PROGMEM char * const PROGMEM dayNames_P[] =
{
dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7
};
const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat";
/* functions to return date strings */
char* monthStr(uint8_t month)
{
strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month])));
return buffer;
}
char* monthShortStr(uint8_t month)
{
for (int i=0; i < dt_SHORT_STR_LEN; i++)
buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)]));
buffer[dt_SHORT_STR_LEN] = 0;
return buffer;
}
char* dayStr(uint8_t day)
{
strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day])));
return buffer;
}
char* dayShortStr(uint8_t day)
{
uint8_t index = day*dt_SHORT_STR_LEN;
for (int i=0; i < dt_SHORT_STR_LEN; i++)
buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i]));
buffer[dt_SHORT_STR_LEN] = 0;
return buffer;
}

Wyświetl plik

@ -0,0 +1,135 @@
Readme file for Arduino Time Library
Time is a library that provides timekeeping functionality for Arduino.
The code is derived from the Playground DateTime library but is updated
to provide an API that is more flexable and easier to use.
A primary goal was to enable date and time functionality that can be used with
a variety of external time sources with minimum differences required in sketch logic.
Example sketches illustrate how similar sketch code can be used with: a Real Time Clock,
internet NTP time service, GPS time data, and Serial time messages from a computer
for time synchronization.
The functions available in the library include:
hour(); // the hour now (0-23)
minute(); // the minute now (0-59)
second(); // the second now (0-59)
day(); // the day now (1-31)
weekday(); // day of the week (1-7), Sunday is day 1
month(); // the month now (1-12)
year(); // the full four digit year: (2009, 2010 etc)
there are also functions to return the hour in 12 hour format
hourFormat12(); // the hour now in 12 hour format
isAM(); // returns true if time now is AM
isPM(); // returns true if time now is PM
now(); // returns the current time as seconds since Jan 1 1970
The time and date functions can take an optional parameter for the time. This prevents
errors if the time rolls over between elements. For example, if a new minute begins
between getting the minute and second, the values will be inconsistent. Using the
following functions eliminates this probglem
time_t t = now(); // store the current time in time variable t
hour(t); // returns the hour for the given time t
minute(t); // returns the minute for the given time t
second(t); // returns the second for the given time t
day(t); // the day for the given time t
weekday(t); // day of the week for the given time t
month(t); // the month for the given time t
year(t); // the year for the given time t
Functions for managing the timer services are:
setTime(t); // set the system time to the give time t
setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr
// (2010 or 10 sets year to 2010)
adjustTime(adjustment); // adjust system time by adding the adjustment value
timeStatus(); // indicates if time has been set and recently synchronized
// returns one of the following enumerations:
timeNotSet // the time has never been set, the clock started at Jan 1 1970
timeNeedsSync // the time had been set but a sync attempt did not succeed
timeSet // the time is set and is synced
Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but
the returned time may have drifted if the status is timeNeedsSync.
setSyncProvider(getTimeFunction); // set the external time provider
setSyncInterval(interval); // set the number of seconds between re-sync
There are many convenience macros in the time.h file for time constants and conversion
of time units.
To use the library, copy the download to the Library directory.
The Time directory contains the Time library and some example sketches
illustrating how the library can be used with various time sources:
- TimeSerial.pde shows Arduino as a clock without external hardware.
It is synchronized by time messages sent over the serial port.
A companion Processing sketch will automatically provide these messages
if it is running and connected to the Arduino serial port.
- TimeSerialDateStrings.pde adds day and month name strings to the sketch above
Short (3 character) and long strings are available to print the days of
the week and names of the months.
- TimeRTC uses a DS1307 real time clock to provide time synchronization.
A basic RTC library named DS1307RTC is included in the download.
To run this sketch the DS1307RTC library must be installed.
- TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock
- TimeRTCLog demonstrates how to calculate the difference between times.
It is a vary simple logger application that monitors events on digtial pins
and prints (to the serial port) the time of an event and the time period since
the previous event.
- TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service.
The NTP protocol uses UDP and the UdpBytewise library is required, see:
http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/
- TimeGPS gets time from a GPS
This requires the TinyGPS library from Mikal Hart:
http://arduiniana.org/libraries/TinyGPS
Differences between this code and the playground DateTime library
although the Time library is based on the DateTime codebase, the API has changed.
Changes in the Time library API:
- time elements are functions returning int (they are variables in DateTime)
- Years start from 1970
- days of the week and months start from 1 (they start from 0 in DateTime)
- DateStrings do not require a seperate library
- time elements can be accessed non-atomically (in DateTime they are always atomic)
- function added to automatically sync time with extrnal source
- localTime and maketime parameters changed, localTime renamed to breakTime
Technical notes:
Internal system time is based on the standard Unix time_t.
The value is the number of seconds since Jan 1 1970.
System time begins at zero when the sketch starts.
The internal time can be automatically synchronized at regular intervals to an external time source.
This is enabled by calling the setSyncProvider(provider) function - the provider argument is
the address of a function that returns the current time as a time_t.
See the sketches in the examples directory for usage.
The default interval for re-syncing the time is 5 minutes but can be changed by calling the
setSyncInterval( interval) method to set the number of seconds between re-sync attempts.
The Time library defines a structure for holding time elements that is a compact version of the C tm structure.
All the members of the Arduino tm structure are bytes and the year is offset from 1970.
Convenience macros provide conversion to and from the Arduino format.
Low level functions to convert between system time and individual time elements are provided:
breakTime(time, &tm); // break time_t into elements stored in tm struct
makeTime(&tm); // return time_t from elements stored in tm struct
The DS1307RTC library included in the download provides an example of how a time provider
can use the low level functions to interface with the Time library.

Wyświetl plik

@ -0,0 +1,321 @@
/*
time.c - low level time and date functions
Copyright (c) Michael Margolis 2009-2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1.0 6 Jan 2010 - initial release
1.1 12 Feb 2010 - fixed leap year calculation error
1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this)
1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
status, updated examples for Arduino 1.0, fixed ARM
compatibility issues, added TimeArduinoDue and TimeTeensy3
examples, add error checking and messages to RTC examples,
add examples to DS1307RTC library.
1.4 5 Sep 2014 - compatibility with Arduino 1.5.7
*/
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include "TimeLib.h"
static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated
static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds
void refreshCache(time_t t) {
if (t != cacheTime) {
breakTime(t, tm);
cacheTime = t;
}
}
int hour() { // the hour now
return hour(now());
}
int hour(time_t t) { // the hour for the given time
refreshCache(t);
return tm.Hour;
}
int hourFormat12() { // the hour now in 12 hour format
return hourFormat12(now());
}
int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
refreshCache(t);
if( tm.Hour == 0 )
return 12; // 12 midnight
else if( tm.Hour > 12)
return tm.Hour - 12 ;
else
return tm.Hour ;
}
uint8_t isAM() { // returns true if time now is AM
return !isPM(now());
}
uint8_t isAM(time_t t) { // returns true if given time is AM
return !isPM(t);
}
uint8_t isPM() { // returns true if PM
return isPM(now());
}
uint8_t isPM(time_t t) { // returns true if PM
return (hour(t) >= 12);
}
int minute() {
return minute(now());
}
int minute(time_t t) { // the minute for the given time
refreshCache(t);
return tm.Minute;
}
int second() {
return second(now());
}
int second(time_t t) { // the second for the given time
refreshCache(t);
return tm.Second;
}
int day(){
return(day(now()));
}
int day(time_t t) { // the day for the given time (0-6)
refreshCache(t);
return tm.Day;
}
int weekday() { // Sunday is day 1
return weekday(now());
}
int weekday(time_t t) {
refreshCache(t);
return tm.Wday;
}
int month(){
return month(now());
}
int month(time_t t) { // the month for the given time
refreshCache(t);
return tm.Month;
}
int year() { // as in Processing, the full four digit year: (2009, 2010 etc)
return year(now());
}
int year(time_t t) { // the year for the given time
refreshCache(t);
return tmYearToCalendar(tm.Year);
}
/*============================================================================*/
/* functions to convert to and from system time */
/* These are for interfacing with time serivces and are not normally needed in a sketch */
// leap year calulator expects year argument as years offset from 1970
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
void breakTime(time_t timeInput, tmElements_t &tm){
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
unsigned long days;
time = (uint32_t)timeInput;
tm.Second = time % 60;
time /= 60; // now it is minutes
tm.Minute = time % 60;
time /= 60; // now it is hours
tm.Hour = time % 24;
time /= 24; // now it is days
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm.Year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days=0;
month=0;
monthLength=0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
} else {
break;
}
}
tm.Month = month + 1; // jan is month 1
tm.Day = time + 1; // day of month
}
time_t makeTime(tmElements_t &tm){
// assemble time elements into time_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
int i;
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds= tm.Year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.Month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
}
}
seconds+= (tm.Day-1) * SECS_PER_DAY;
seconds+= tm.Hour * SECS_PER_HOUR;
seconds+= tm.Minute * SECS_PER_MIN;
seconds+= tm.Second;
return (time_t)seconds;
}
/*=====================================================*/
/* Low level system time functions */
static uint32_t sysTime = 0;
static uint32_t prevMillis = 0;
static uint32_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;
getExternalTime getTimePtr; // pointer to external sync function
//setExternalTime setTimePtr; // not used in this version
#ifdef TIME_DRIFT_INFO // define this to get drift data
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
#endif
time_t now() {
// calculate number of seconds passed since last call to now()
while (millis() - prevMillis >= 1000) {
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++;
prevMillis += 1000;
#ifdef TIME_DRIFT_INFO
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
#endif
}
if (nextSyncTime <= sysTime) {
if (getTimePtr != 0) {
time_t t = getTimePtr();
if (t != 0) {
setTime(t);
} else {
nextSyncTime = sysTime + syncInterval;
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
}
}
}
return (time_t)sysTime;
}
void setTime(time_t t) {
#ifdef TIME_DRIFT_INFO
if(sysUnsyncedTime == 0)
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
// year can be given as full four digit year or two digts (2010 or 10 for 2010);
//it is converted to years since 1970
if( yr > 99)
yr = yr - 1970;
else
yr += 30;
tm.Year = yr;
tm.Month = mnth;
tm.Day = dy;
tm.Hour = hr;
tm.Minute = min;
tm.Second = sec;
setTime(makeTime(tm));
}
void adjustTime(long adjustment) {
sysTime += adjustment;
}
// indicates if time has been set and recently synchronized
timeStatus_t timeStatus() {
now(); // required to actually update the status
return Status;
}
void setSyncProvider( getExternalTime getTimeFunction){
getTimePtr = getTimeFunction;
nextSyncTime = sysTime;
now(); // this will sync the clock
}
void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
syncInterval = (uint32_t)interval;
nextSyncTime = sysTime + syncInterval;
}

Wyświetl plik

@ -0,0 +1 @@
#include "TimeLib.h"

Wyświetl plik

@ -0,0 +1,144 @@
/*
time.h - low level time and date functions
*/
/*
July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
- fixed daysToTime_t macro (thanks maniacbug)
*/
#ifndef _Time_h
#ifdef __cplusplus
#define _Time_h
#include <inttypes.h>
#ifndef __AVR__
#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
#endif
#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
typedef unsigned long time_t;
#endif
// This ugly hack allows us to define C++ overloaded functions, when included
// from within an extern "C", as newlib's sys/stat.h does. Actually it is
// intended to include "time.h" from the C library (on ARM, but AVR does not
// have that file at all). On Mac and Windows, the compiler will find this
// "Time.h" instead of the C library "time.h", so we may cause other weird
// and unpredictable effects by conflicting with the C library header "time.h",
// but at least this hack lets us define C++ functions as intended. Hopefully
// nothing too terrible will result from overriding the C library header?!
extern "C++" {
typedef enum {timeNotSet, timeNeedsSync, timeSet
} timeStatus_t ;
typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;
typedef enum {
tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields
} tmByteFields;
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
} tmElements_t, TimeElements, *tmElementsPtr_t;
//convenience macros to convert to and from tm years
#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year
#define CalendarYrToTm(Y) ((Y) - 1970)
#define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000
#define y2kYearToTm(Y) ((Y) + 30)
typedef time_t(*getExternalTime)();
//typedef void (*setExternalTime)(const time_t); // not used in this version
/*==============================================================================*/
/* Useful Constants */
#define SECS_PER_MIN ((time_t)(60UL))
#define SECS_PER_HOUR ((time_t)(3600UL))
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
#define DAYS_PER_WEEK ((time_t)(7UL))
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
#define SECS_PER_YEAR ((time_t)(SECS_PER_WEEK * 52UL))
#define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight
// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
// Always set the correct time before settting alarms
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1
#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time
/* Useful Macros for converting elapsed time to a time_t */
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)
#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR)
#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011
#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK)
/*============================================================================*/
/* time and date functions */
int hour(); // the hour now
int hour(time_t t); // the hour for the given time
int hourFormat12(); // the hour now in 12 hour format
int hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM(); // returns true if time now is AM
uint8_t isAM(time_t t); // returns true the given time is AM
uint8_t isPM(); // returns true if time now is PM
uint8_t isPM(time_t t); // returns true the given time is PM
int minute(); // the minute now
int minute(time_t t); // the minute for the given time
int second(); // the second now
int second(time_t t); // the second for the given time
int day(); // the day now
int day(time_t t); // the day for the given time
int weekday(); // the weekday now (Sunday is day 1)
int weekday(time_t t); // the weekday for the given time
int month(); // the month now (Jan is month 1)
int month(time_t t); // the month for the given time
int year(); // the full four digit year: (2009, 2010 etc)
int year(time_t t); // the year for the given time
time_t now(); // return the current time as seconds since Jan 1 1970
void setTime(time_t t);
void setTime(int hr,int min,int sec,int day, int month, int yr);
void adjustTime(long adjustment);
/* date strings */
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
char* monthStr(uint8_t month);
char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day);
/* time sync functions */
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
/* low level functions to convert to and from system time */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
time_t makeTime(tmElements_t &tm); // convert time elements into time_t
} // extern "C++"
#endif // __cplusplus
#endif /* _Time_h */

Wyświetl plik

@ -0,0 +1,78 @@
/**
* SyncArduinoClock.
*
* portIndex must be set to the port connected to the Arduino
*
* The current time is sent in response to request message from Arduino
* or by clicking the display window
*
* The time message is 11 ASCII text characters; a header (the letter 'T')
* followed by the ten digit system time (unix time)
*/
import processing.serial.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public static final short portIndex = 0; // select the com port, 0 is the first port
public static final String TIME_HEADER = "T"; //header for arduino serial time message
public static final char TIME_REQUEST = 7; // ASCII bell character
public static final char LF = 10; // ASCII linefeed
public static final char CR = 13; // ASCII linefeed
Serial myPort; // Create object from Serial class
void setup() {
size(200, 200);
println(Serial.list());
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this,Serial.list()[portIndex], 9600);
println(getTimeNow());
}
void draw()
{
textSize(20);
textAlign(CENTER);
fill(0);
text("Click to send\nTime Sync", 0, 75, 200, 175);
if ( myPort.available() > 0) { // If data is available,
char val = char(myPort.read()); // read it and store it in val
if(val == TIME_REQUEST){
long t = getTimeNow();
sendTimeMessage(TIME_HEADER, t);
}
else
{
if(val == LF)
; //igonore
else if(val == CR)
println();
else
print(val); // echo everying but time request
}
}
}
void mousePressed() {
sendTimeMessage( TIME_HEADER, getTimeNow());
}
void sendTimeMessage(String header, long time) {
String timeStr = String.valueOf(time);
myPort.write(header); // send header and time to arduino
myPort.write(timeStr);
myPort.write('\n');
}
long getTimeNow(){
// java time is in ms, we want secs
Date d = new Date();
Calendar cal = new GregorianCalendar();
long current = d.getTime()/1000;
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
long daylight = cal.get(cal.DST_OFFSET)/1000;
return current + timezone + daylight;
}

Wyświetl plik

@ -0,0 +1,9 @@
SyncArduinoClock is a Processing sketch that responds to Arduino requests for
time synchronization messages.
The portIndex must be set the Serial port connected to Arduino.
Download TimeSerial.pde onto Arduino and you should see the time
message displayed when you run SyncArduinoClock in Processing.
The Arduino time is set from the time on your computer through the
Processing sketch.

Wyświetl plik

@ -0,0 +1,71 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
* This example requires Markus Lange's Arduino Due RTC Library
* https://github.com/MarkusLange/Arduino-Due-RTC-Library
*/
#include <TimeLib.h>
#include <rtc_clock.h>
// Select the Slowclock source
//RTC_clock rtc_clock(RC);
RTC_clock rtc_clock(XTAL);
void setup() {
Serial.begin(9600);
rtc_clock.init();
if (rtc_clock.date_already_set() == 0) {
// Unfortunately, the Arduino Due hardware does not seem to
// be designed to maintain the RTC clock state when the
// board resets. Markus described it thusly: "Uhh the Due
// does reset with the NRSTB pin. This resets the full chip
// with all backup regions including RTC, RTT and SC. Only
// if the reset is done with the NRST pin will these regions
// stay with their old values."
rtc_clock.set_time(__TIME__);
rtc_clock.set_date(__DATE__);
// However, this might work on other unofficial SAM3X boards
// with different reset circuitry than Arduino Due?
}
setSyncProvider(getArduinoDueTime);
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
time_t getArduinoDueTime()
{
return rtc_clock.unixtime();
}
void loop()
{
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

Wyświetl plik

@ -0,0 +1,87 @@
/*
* TimeGPS.pde
* example code illustrating time synced from a GPS
*
*/
#include <TimeLib.h>
#include <TinyGPS.h> // http://arduiniana.org/libraries/TinyGPS/
#include <SoftwareSerial.h>
// TinyGPS and SoftwareSerial libraries are the work of Mikal Hart
SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10
TinyGPS gps;
// To use a hardware serial port, which is far more efficient than
// SoftwareSerial, uncomment this line and remove SoftwareSerial
//#define SerialGPS Serial1
// Offset hours from gps time (UTC)
const int offset = 1; // Central European Time
//const int offset = -5; // Eastern Standard Time (USA)
//const int offset = -4; // Eastern Daylight Time (USA)
//const int offset = -8; // Pacific Standard Time (USA)
//const int offset = -7; // Pacific Daylight Time (USA)
// Ideally, it should be possible to learn the time zone
// based on the GPS position data. However, that would
// require a complex library, probably incorporating some
// sort of database using Eric Muller's time zone shape
// maps, at http://efele.net/maps/tz/
time_t prevDisplay = 0; // when the digital clock was displayed
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
SerialGPS.begin(4800);
Serial.println("Waiting for GPS time ... ");
}
void loop()
{
while (SerialGPS.available()) {
if (gps.encode(SerialGPS.read())) { // process gps messages
// when TinyGPS reports new data...
unsigned long age;
int Year;
byte Month, Day, Hour, Minute, Second;
gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age);
if (age < 500) {
// set the Time to the latest GPS reading
setTime(Hour, Minute, Second, Day, Month, Year);
adjustTime(offset * SECS_PER_HOUR);
}
}
}
if (timeStatus()!= timeNotSet) {
if (now() != prevDisplay) { //update the display only if the time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

Wyświetl plik

@ -0,0 +1,135 @@
/*
* Time_NTP.pde
* Example showing time sync to NTP time source
*
* This sketch uses the Ethernet library
*/
#include <TimeLib.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// NTP Servers:
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
const int timeZone = 1; // Central European Time
//const int timeZone = -5; // Eastern Standard Time (USA)
//const int timeZone = -4; // Eastern Daylight Time (USA)
//const int timeZone = -8; // Pacific Standard Time (USA)
//const int timeZone = -7; // Pacific Daylight Time (USA)
EthernetUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("TimeNTP Example");
if (Ethernet.begin(mac) == 0) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println("Failed to configure Ethernet using DHCP");
delay(10000);
}
}
Serial.print("IP number assigned by DHCP is ");
Serial.println(Ethernet.localIP());
Udp.begin(localPort);
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
}
time_t prevDisplay = 0; // when the digital clock was displayed
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}

Wyświetl plik

@ -0,0 +1,156 @@
/*
* TimeNTP_ESP8266WiFi.ino
* Example showing time sync to NTP time source
*
* This sketch uses the ESP8266WiFi library
*/
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char ssid[] = "*************"; // your network SSID (name)
const char pass[] = "********"; // your network password
// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";
const int timeZone = 1; // Central European Time
//const int timeZone = -5; // Eastern Standard Time (USA)
//const int timeZone = -4; // Eastern Daylight Time (USA)
//const int timeZone = -8; // Pacific Standard Time (USA)
//const int timeZone = -7; // Pacific Daylight Time (USA)
WiFiUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("TimeNTP Example");
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP number assigned by DHCP is ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(Udp.localPort());
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
setSyncInterval(300);
}
time_t prevDisplay = 0; // when the digital clock was displayed
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay()
{
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(".");
Serial.print(month());
Serial.print(".");
Serial.print(year());
Serial.println();
}
void printDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}

Wyświetl plik

@ -0,0 +1,55 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
void setup() {
Serial.begin(9600);
while (!Serial) ; // wait until Arduino Serial Monitor opens
setSyncProvider(RTC.get); // the function to get the time from the RTC
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
void loop()
{
if (timeStatus() == timeSet) {
digitalClockDisplay();
} else {
Serial.println("The time has not been set. Please run the Time");
Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
Serial.println();
delay(4000);
}
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

Wyświetl plik

@ -0,0 +1,107 @@
/*
* TimeRTCLogger.pde
* example code illustrating adding and subtracting Time.
*
* this sketch logs pin state change events
* the time of the event and time since the previous event is calculated and sent to the serial port.
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
const int nbrInputPins = 6; // monitor 6 digital pins
const int inputPins[nbrInputPins] = {2,3,4,5,6,7}; // pins to monitor
boolean state[nbrInputPins] ; // the state of the monitored pins
time_t prevEventTime[nbrInputPins] ; // the time of the previous event
void setup() {
Serial.begin(9600);
setSyncProvider(RTC.get); // the function to sync the time from the RTC
for(int i=0; i < nbrInputPins; i++){
pinMode( inputPins[i], INPUT);
// uncomment these lines if pull-up resistors are wanted
// pinMode( inputPins[i], INPUT_PULLUP);
// state[i] = HIGH;
}
}
void loop()
{
for(int i=0; i < nbrInputPins; i++)
{
boolean val = digitalRead(inputPins[i]);
if(val != state[i])
{
time_t duration = 0; // the time since the previous event
state[i] = val;
time_t timeNow = now();
if(prevEventTime[i] > 0)
// if this was not the first state change, calculate the time from the previous change
duration = duration = timeNow - prevEventTime[i];
logEvent(inputPins[i], val, timeNow, duration ); // log the event
prevEventTime[i] = timeNow; // store the time for this event
}
}
}
void logEvent( int pin, boolean state, time_t timeNow, time_t duration)
{
Serial.print("Pin ");
Serial.print(pin);
if( state == HIGH)
Serial.print(" went High at ");
else
Serial.print(" went Low at ");
showTime(timeNow);
if(duration > 0){
// only display duration if greater than 0
Serial.print(", Duration was ");
showDuration(duration);
}
Serial.println();
}
void showTime(time_t t){
// display the given time
Serial.print(hour(t));
printDigits(minute(t));
printDigits(second(t));
Serial.print(" ");
Serial.print(day(t));
Serial.print(" ");
Serial.print(month(t));
Serial.print(" ");
Serial.print(year(t));
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void showDuration(time_t duration){
// prints the duration in days, hours, minutes and seconds
if(duration >= SECS_PER_DAY){
Serial.print(duration / SECS_PER_DAY);
Serial.print(" day(s) ");
duration = duration % SECS_PER_DAY;
}
if(duration >= SECS_PER_HOUR){
Serial.print(duration / SECS_PER_HOUR);
Serial.print(" hour(s) ");
duration = duration % SECS_PER_HOUR;
}
if(duration >= SECS_PER_MIN){
Serial.print(duration / SECS_PER_MIN);
Serial.print(" minute(s) ");
duration = duration % SECS_PER_MIN;
}
Serial.print(duration);
Serial.print(" second(s) ");
}

Wyświetl plik

@ -0,0 +1,80 @@
/*
* TimeRTCSet.pde
* example code illustrating Time library with Real Time Clock.
*
* RTC clock is set in response to serial port time message
* A Processing example sketch to set the time is included in the download
* On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
void loop()
{
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
RTC.set(t); // set the RTC and the system time to the received value
setTime(t);
}
}
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}

Wyświetl plik

@ -0,0 +1,81 @@
/*
* TimeSerial.pde
* example code illustrating Time library set through serial port messages.
*
* Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970)
* you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013
T1357041600
*
* A Processing example sketch to automatically send the messages is included in the download
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
*/
#include <TimeLib.h>
#define TIME_HEADER "T" // Header tag for serial time sync message
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
pinMode(13, OUTPUT);
setSyncProvider( requestSync); //set function to call when sync required
Serial.println("Waiting for sync message");
}
void loop(){
if (Serial.available()) {
processSyncMessage();
}
if (timeStatus()!= timeNotSet) {
digitalClockDisplay();
}
if (timeStatus() == timeSet) {
digitalWrite(13, HIGH); // LED on if synced
} else {
digitalWrite(13, LOW); // LED off if needs refresh
}
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void processSyncMessage() {
unsigned long pctime;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
setTime(pctime); // Sync Arduino clock to the time received on the serial port
}
}
}
time_t requestSync()
{
Serial.write(TIME_REQUEST);
return 0; // the time will be sent later in response to serial mesg
}

Wyświetl plik

@ -0,0 +1,108 @@
/*
* TimeSerialDateStrings.pde
* example code illustrating Time library date strings
*
* This sketch adds date string functionality to TimeSerial sketch
* Also shows how to handle different messages
*
* A message starting with a time header sets the time
* A Processing example sketch to automatically send the messages is inclided in the download
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
*
* A message starting with a format header sets the date format
* send: Fs\n for short date format
* send: Fl\n for long date format
*/
#include <TimeLib.h>
// single character message tags
#define TIME_HEADER 'T' // Header tag for serial time sync message
#define FORMAT_HEADER 'F' // Header tag indicating a date format message
#define FORMAT_SHORT 's' // short month and day strings
#define FORMAT_LONG 'l' // (lower case l) long month and day strings
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
static boolean isLongFormat = true;
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
setSyncProvider( requestSync); //set function to call when sync required
Serial.println("Waiting for sync message");
}
void loop(){
if (Serial.available() > 1) { // wait for at least two characters
char c = Serial.read();
if( c == TIME_HEADER) {
processSyncMessage();
}
else if( c== FORMAT_HEADER) {
processFormatMessage();
}
}
if (timeStatus()!= timeNotSet) {
digitalClockDisplay();
}
delay(1000);
}
void digitalClockDisplay() {
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
if(isLongFormat)
Serial.print(dayStr(weekday()));
else
Serial.print(dayShortStr(weekday()));
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
if(isLongFormat)
Serial.print(monthStr(month()));
else
Serial.print(monthShortStr(month()));
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void processFormatMessage() {
char c = Serial.read();
if( c == FORMAT_LONG){
isLongFormat = true;
Serial.println(F("Setting long format"));
}
else if( c == FORMAT_SHORT) {
isLongFormat = false;
Serial.println(F("Setting short format"));
}
}
void processSyncMessage() {
unsigned long pctime;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 - paul, perhaps we define in time.h?
pctime = Serial.parseInt();
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
setTime(pctime); // Sync Arduino clock to the time received on the serial port
}
}
time_t requestSync() {
Serial.write(TIME_REQUEST);
return 0; // the time will be sent later in response to serial mesg
}

Wyświetl plik

@ -0,0 +1,78 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
*/
#include <TimeLib.h>
void setup() {
// set the Time library to use Teensy 3.0's RTC to keep time
setSyncProvider(getTeensy3Time);
Serial.begin(115200);
while (!Serial); // Wait for Arduino Serial Monitor to open
delay(100);
if (timeStatus()!= timeSet) {
Serial.println("Unable to sync with the RTC");
} else {
Serial.println("RTC has set the system time");
}
}
void loop() {
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
Teensy3Clock.set(t); // set the RTC
setTime(t);
}
}
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay() {
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

Wyświetl plik

@ -0,0 +1,34 @@
#######################################
# Syntax Coloring Map For Time
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
time_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
now KEYWORD2
second KEYWORD2
minute KEYWORD2
hour KEYWORD2
day KEYWORD2
month KEYWORD2
year KEYWORD2
isAM KEYWORD2
isPM KEYWORD2
weekday KEYWORD2
setTime KEYWORD2
adjustTime KEYWORD2
setSyncProvider KEYWORD2
setSyncInterval KEYWORD2
timeStatus KEYWORD2
TimeLib KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################

Wyświetl plik

@ -0,0 +1,26 @@
{
"name": "Time",
"description": "Time keeping library",
"keywords": "Time, date, hour, minute, second, day, week, month, year, RTC",
"authors": [
{
"name": "Michael Margolis"
},
{
"name": "Paul Stoffregen",
"email": "paul@pjrc.com",
"url": "http://www.pjrc.com",
"maintainer": true
}
],
"repository": {
"type": "git",
"url": "https://github.com/PaulStoffregen/Time"
},
"version": "1.5",
"homepage": "http://playground.arduino.cc/Code/Time",
"frameworks": "Arduino",
"examples": [
"examples/*/*.ino"
]
}

Wyświetl plik

@ -0,0 +1,10 @@
name=Time
version=1.5
author=Michael Margolis
maintainer=Paul Stoffregen
sentence=Timekeeping functionality for Arduino
paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet). This library is often used together with TimeAlarms and DS1307RTC.
category=Timing
url=http://playground.arduino.cc/code/time
architectures=*

Some files were not shown because too many files have changed in this diff Show More