This marks my 500th post in the blog. Not bad, considering most of the posts are projects, that is a lot of soldering.
Carrying on, I had been experimenting with GPS disciplined oscillators, most using a control loop for a 10Mhz crystal oscillator using 10Khz output from a GPS module PPS. I was not happy with results so decided to go on a different approach.
The NEO7M gps module can output multiple frequencies, the problem is how clean the output is, eventually figure out that a 24Mhz output is enough clean to drive the Si5352 module XO input, this module according to specs can take a crystal of 25 or 27, I tried using external 24Mhz as reference and worked brilliant.
The simplified schematic:
There's also an LED to Arduino pin 10 to indicate satellite lock
Since there are multiple outputs on the Si5351 I used another output (CLK0) for a 1Mhz out besides the 10Mhz one (CLK1).
I didn't used an LCD display on my project to keep consumption as low as possible.
The 10Mhz output and the GPS antenna input are on the back:
All powered by a 9V battery for portable use.
Inside view:
You need to remove the inboard oscillator crystal from the Si5351 module and connect XA pin to the 24Mhz source from the GPS module.
From datasheet
XB is not used.
The lib's for the Si5351, when programing/compiling the code, need to reflect the new 24Mhz clock input.
Library file:
Si5351/si5351.h
Change from 25Mhz to 24 as bellow:
#define SI5351_XTAL_FREQ 24000000
The 1Mhz output from the Si5351 is not filtered so plenty of harmonics so it can use as marker.
The final 10Mhz output: is filtered trough a low pass filter to clear some noise and this is the result:
Checking the accuracy of my frequency counter (since I know the GPS is correct):
It's counting 10000000.3 Hz so 0.3 Hz off, for a counter with 20 Year of age (or so) is not bad.
The code:
It basically programs the u-blox 7m to out a 24Mhz signal on PPS output.
It display all those steps on i2c LCD if connected and just for monitoring
lock LED indication is on when enough satelites are in sync, never the less from the boot it starts output the 24Mhz that might not be stable enough before lock/satelites avaiable but still usable.
// programing of the u-blox 7m module from Arduino for timepulse TP-5
// hex was taken from u-center software configuration view
//
//
// plus, flashing LED connected to pin 10 indicates when enough sat's in view
// u-blox module to connect o 3 and 4 for using soft serial of Arduino
// CT2GQV 2019 mixing multiple libs and examples.
#include<SoftwareSerial.h>
#include <TinyGPS.h>
#include "si5351.h"
Si5351 si5351; // i2c 0x60
#include
#include
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);
const char UBLOX_INIT[] PROGMEM = {
// the actual programing string, uncoment for the one needed. 10Mhz, 2.5Mhz, 24Mhz or 2Mhz
// any frequency not integer divide of 48Mhz will have some jitter since module reference is 48. Best use is for 24 or 2 Mhz
/*
// CFG-TP5 1Hz / 10Mhz sync
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x96,
0x98, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x08, 0x00, 0x00, 0x7E, 0xA8,
*/
/*
// CFG-TP5 1Hz / 10Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x96, 0x98, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xA8, 0x08,
*/
/*
// CFG-TP5 1Hz / 24 Mhz no sync 0ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6E, 0x01,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6D, 0x8D,
*/
// CFG-TP5 24MHz / 24 Mhz - allways outputing 24Mhz either in sync with GPS or not. LED on D10 will indicate GPS lock.
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6E, 0x01, 0x00, 0x36,
0x6E, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x11, 0xD8,
/*
// CFG-TP5 1Hz / 2.5Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x25, 0x26, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xE5, 0x21,
*/
/*
// CFG-TP5 1Hz / 2 Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x84, 0x1E, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x1C, 0x1E,
*/
/*
// UBX-CFG-TP5
0xB5, 0x62, // header
0x06, 0x31, // time pulse get/set
0x20, // lenght 32
0x00, // tpIdx time pulse selection = 0 = timepulse, 1 = timepulse2 (U1 Char)
0x00, // reserved0 U1
0x01, 0x00, // reserved1 U2
0x00, 0x32, // antCableDelay ns
0x00, 0x00, // rf group delay I2
0x00, 0x90, 0xD0, 0x03, // freqPeriod
0x00, 0x40, 0x42, 0x0F, // freqPeriodLoc
0x00, 0xF0, 0x49, 0x02, // pulselenRatio
0x00, 0x60, 0xAE, 0x0A, // pulselenRatio
0x00, 0x00, 0x00, 0x00, // userConfigDelay ns
0x00, 0x77, 0x00, 0x00, // flags - page 135 u-blox 7 Receiver Description Including Protocol Specification V14.pdf
0x00, 0x48, 0x65,
*/
};
int SATLED = 10; // for showing we have statelites and in sync
int havefix = 0; // to keep track if we have fix for at least 3 seconds
float start_freq = 2000000000;
TinyGPS gps;
// SoftwareSerial ss(4, 3);
SoftwareSerial ss(3, 4);
int sats = 0 ;
void setup()
{
lcd.begin(16, 2); // LCD set for 16 by 2 display
lcd.setBacklightPin(3,POSITIVE); // (BL, BL_POL)
lcd.setBacklight(HIGH); // LCD backlight turned ON
lcd.setCursor(0, 0); //
lcd.print("Booting...");
ss.begin(9600);
pinMode(SATLED, OUTPUT); // to indicate we have enough satelites
digitalWrite(SATLED, HIGH);
delay(2000);
digitalWrite(SATLED, LOW);
// actual u-blox 7m programing
for(int i = 0; i < sizeof(UBLOX_INIT); i++) {
ss.write( pgm_read_byte(UBLOX_INIT+i) );
delay(5); // simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
}
// ends here
Serial.begin(9600);
Serial.println("= sent init string to GPS =");
lcd.setCursor(0, 0); lcd.print("NO DATA ");
// on the library Si5351/si5351.h chang it to 24Mhz
// #define SI5351_XTAL_FREQ 25000000
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(10000000, 0, SI5351_CLK1); // 10.000 Mhz for the ext 10Mhz of counter
si5351.set_freq( 1000000, 0, SI5351_CLK0); // 1.000 Mhz for the marker
}
void loop()
{
bool newData = false;
unsigned long chars;
unsigned short sentences, failed;
// For one second we parse GPS data and report some key values
for (unsigned long start = millis(); millis() - start < 1000;)
{
while (ss.available())
{
char c = ss.read();
Serial.write(c); // uncomment this line if you want to see the GPS data flowing
if (gps.encode(c)) // Did a new valid sentence come in?
newData = true;
}
}
if (newData)
{
float flat, flon;
unsigned long age;
gps.f_get_position(&flat, &flon, &age);
Serial.print("LAT=");
Serial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);
Serial.print(" LON=");
Serial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);
Serial.print(" SAT=");
sats = gps.satellites();
Serial.println(sats);
// lcd.setCursor(0, 0); lcd.print("Satelites:");lcd.print(sats);
if (sats >= 3){ // we have sats in view, we should be good
digitalWrite(SATLED, HIGH); // delay(200);digitalWrite(SATLED, LOW );
havefix=2;
havefix++; // increments every time we have fix until 3, if we don't have fix/sats then after 3 cycles it will go zero and we shutt the indicator led.
if (havefix >= 3)havefix=3; // never goes over 3
Serial.println(); Serial.print("SAT_HAVE_FIX: "); Serial.println(havefix);
};
} // end new data
if (havefix >= 1) havefix--; // we had allready 1 fix in the loop
if (havefix==0) { digitalWrite(SATLED, LOW ); }; // we don't have fix so power off the fix LED
Serial.print("MAIN_HAVE_FIX: "); Serial.println(havefix);
gps.stats(&chars, &sentences, &failed);
if (chars == 0)
Serial.println("** No characters received from GPS: check wiring **");
}
//=end code==============
I've seen other type of approach for similar problem using just a 1 Hz PPS and then integrating and correcting in software the "error" from one of the clock outputs of the Si5351, this is also a valid approach if your GPS module can only do 1Hz PPS out, like on this ARRL article:
http://www.arrl.org/files/file/QEX_Next_Issue/2015/Jul-Aug_2015/Marcus.pdf
P.S.
Carrying on, I had been experimenting with GPS disciplined oscillators, most using a control loop for a 10Mhz crystal oscillator using 10Khz output from a GPS module PPS. I was not happy with results so decided to go on a different approach.
The NEO7M gps module can output multiple frequencies, the problem is how clean the output is, eventually figure out that a 24Mhz output is enough clean to drive the Si5352 module XO input, this module according to specs can take a crystal of 25 or 27, I tried using external 24Mhz as reference and worked brilliant.
The simplified schematic:
There's also an LED to Arduino pin 10 to indicate satellite lock
Since there are multiple outputs on the Si5351 I used another output (CLK0) for a 1Mhz out besides the 10Mhz one (CLK1).
I didn't used an LCD display on my project to keep consumption as low as possible.
The 10Mhz output and the GPS antenna input are on the back:
All powered by a 9V battery for portable use.
Inside view:
You need to remove the inboard oscillator crystal from the Si5351 module and connect XA pin to the 24Mhz source from the GPS module.
From datasheet
XB is not used.
The lib's for the Si5351, when programing/compiling the code, need to reflect the new 24Mhz clock input.
Library file:
Si5351/si5351.h
Change from 25Mhz to 24 as bellow:
#define SI5351_XTAL_FREQ 24000000
The 1Mhz output from the Si5351 is not filtered so plenty of harmonics so it can use as marker.
The final 10Mhz output: is filtered trough a low pass filter to clear some noise and this is the result:
Checking the accuracy of my frequency counter (since I know the GPS is correct):
It's counting 10000000.3 Hz so 0.3 Hz off, for a counter with 20 Year of age (or so) is not bad.
The code:
It basically programs the u-blox 7m to out a 24Mhz signal on PPS output.
It display all those steps on i2c LCD if connected and just for monitoring
lock LED indication is on when enough satelites are in sync, never the less from the boot it starts output the 24Mhz that might not be stable enough before lock/satelites avaiable but still usable.
// programing of the u-blox 7m module from Arduino for timepulse TP-5
// hex was taken from u-center software configuration view
//
//
// plus, flashing LED connected to pin 10 indicates when enough sat's in view
// u-blox module to connect o 3 and 4 for using soft serial of Arduino
// CT2GQV 2019 mixing multiple libs and examples.
#include
#include <TinyGPS.h>
#include "si5351.h"
Si5351 si5351; // i2c 0x60
#include
#include
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);
const char UBLOX_INIT[] PROGMEM = {
// the actual programing string, uncoment for the one needed. 10Mhz, 2.5Mhz, 24Mhz or 2Mhz
// any frequency not integer divide of 48Mhz will have some jitter since module reference is 48. Best use is for 24 or 2 Mhz
/*
// CFG-TP5 1Hz / 10Mhz sync
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x96,
0x98, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x08, 0x00, 0x00, 0x7E, 0xA8,
*/
/*
// CFG-TP5 1Hz / 10Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x96, 0x98, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xA8, 0x08,
*/
/*
// CFG-TP5 1Hz / 24 Mhz no sync 0ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6E, 0x01,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6D, 0x8D,
*/
// CFG-TP5 24MHz / 24 Mhz - allways outputing 24Mhz either in sync with GPS or not. LED on D10 will indicate GPS lock.
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6E, 0x01, 0x00, 0x36,
0x6E, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x11, 0xD8,
/*
// CFG-TP5 1Hz / 2.5Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x25, 0x26, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xE5, 0x21,
*/
/*
// CFG-TP5 1Hz / 2 Mhz no sync 50ms cable delay
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x84, 0x1E, 0x00,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x1C, 0x1E,
*/
/*
// UBX-CFG-TP5
0xB5, 0x62, // header
0x06, 0x31, // time pulse get/set
0x20, // lenght 32
0x00, // tpIdx time pulse selection = 0 = timepulse, 1 = timepulse2 (U1 Char)
0x00, // reserved0 U1
0x01, 0x00, // reserved1 U2
0x00, 0x32, // antCableDelay ns
0x00, 0x00, // rf group delay I2
0x00, 0x90, 0xD0, 0x03, // freqPeriod
0x00, 0x40, 0x42, 0x0F, // freqPeriodLoc
0x00, 0xF0, 0x49, 0x02, // pulselenRatio
0x00, 0x60, 0xAE, 0x0A, // pulselenRatio
0x00, 0x00, 0x00, 0x00, // userConfigDelay ns
0x00, 0x77, 0x00, 0x00, // flags - page 135 u-blox 7 Receiver Description Including Protocol Specification V14.pdf
0x00, 0x48, 0x65,
*/
};
int SATLED = 10; // for showing we have statelites and in sync
int havefix = 0; // to keep track if we have fix for at least 3 seconds
float start_freq = 2000000000;
TinyGPS gps;
// SoftwareSerial ss(4, 3);
SoftwareSerial ss(3, 4);
int sats = 0 ;
void setup()
{
lcd.begin(16, 2); // LCD set for 16 by 2 display
lcd.setBacklightPin(3,POSITIVE); // (BL, BL_POL)
lcd.setBacklight(HIGH); // LCD backlight turned ON
lcd.setCursor(0, 0); //
lcd.print("Booting...");
ss.begin(9600);
pinMode(SATLED, OUTPUT); // to indicate we have enough satelites
digitalWrite(SATLED, HIGH);
delay(2000);
digitalWrite(SATLED, LOW);
// actual u-blox 7m programing
for(int i = 0; i < sizeof(UBLOX_INIT); i++) {
ss.write( pgm_read_byte(UBLOX_INIT+i) );
delay(5); // simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
}
// ends here
Serial.begin(9600);
Serial.println("= sent init string to GPS =");
lcd.setCursor(0, 0); lcd.print("NO DATA ");
// on the library Si5351/si5351.h chang it to 24Mhz
// #define SI5351_XTAL_FREQ 25000000
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(10000000, 0, SI5351_CLK1); // 10.000 Mhz for the ext 10Mhz of counter
si5351.set_freq( 1000000, 0, SI5351_CLK0); // 1.000 Mhz for the marker
}
void loop()
{
bool newData = false;
unsigned long chars;
unsigned short sentences, failed;
// For one second we parse GPS data and report some key values
for (unsigned long start = millis(); millis() - start < 1000;)
{
while (ss.available())
{
char c = ss.read();
Serial.write(c); // uncomment this line if you want to see the GPS data flowing
if (gps.encode(c)) // Did a new valid sentence come in?
newData = true;
}
}
if (newData)
{
float flat, flon;
unsigned long age;
gps.f_get_position(&flat, &flon, &age);
Serial.print("LAT=");
Serial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);
Serial.print(" LON=");
Serial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);
Serial.print(" SAT=");
sats = gps.satellites();
Serial.println(sats);
// lcd.setCursor(0, 0); lcd.print("Satelites:");lcd.print(sats);
if (sats >= 3){ // we have sats in view, we should be good
digitalWrite(SATLED, HIGH); // delay(200);digitalWrite(SATLED, LOW );
havefix=2;
havefix++; // increments every time we have fix until 3, if we don't have fix/sats then after 3 cycles it will go zero and we shutt the indicator led.
if (havefix >= 3)havefix=3; // never goes over 3
Serial.println(); Serial.print("SAT_HAVE_FIX: "); Serial.println(havefix);
};
} // end new data
if (havefix >= 1) havefix--; // we had allready 1 fix in the loop
if (havefix==0) { digitalWrite(SATLED, LOW ); }; // we don't have fix so power off the fix LED
Serial.print("MAIN_HAVE_FIX: "); Serial.println(havefix);
gps.stats(&chars, &sentences, &failed);
if (chars == 0)
Serial.println("** No characters received from GPS: check wiring **");
}
//=end code==============
I've seen other type of approach for similar problem using just a 1 Hz PPS and then integrating and correcting in software the "error" from one of the clock outputs of the Si5351, this is also a valid approach if your GPS module can only do 1Hz PPS out, like on this ARRL article:
http://www.arrl.org/files/file/QEX_Next_Issue/2015/Jul-Aug_2015/Marcus.pdf
P.S.
I was not having great reception with the external antenna near a window, only on the clear, looks like that both antennas in parallel (internal/external) will cause some performance issues. For use of the external antenna only, remove C2 from the board:
From the schematic at: https://wiki.keyestudio.com/KS0319_keyestudio_GPS_Module
Much faster now on gps lock acquire.
Have a nice day!
From the schematic at: https://wiki.keyestudio.com/KS0319_keyestudio_GPS_Module
Much faster now on gps lock acquire.
Have a nice day!