From 70c9003b5e1c4d46fa9ed6289bae2cabba84cc63 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 5 Jul 2018 21:20:11 -0500 Subject: [PATCH 1/6] Implement 'switch to passthrough' mode --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index 985a752..5b8589a 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -14,7 +14,7 @@ SoftwareSerial gpsPort(2, 3); // RX, TX #define LEDIndicator1 5 //LED indicator for GPS Lock on pin A3 #define FIXOut 4 //Pin Out at IDC. Indicator for GPS Lock on pin 4 #define LDO_Enable A3 //GPS Voltage regulator Enable on pin A3 -boolean GPSOK; +boolean GPSOK, passthrough; const char softwareversion[] = "1.05" ; //Version of this program, sent to serialport at startup NMEAGPS gps; // This parses the GPS characters gps_fix fix; // This holds on to the latest values @@ -74,11 +74,27 @@ void setup() //-------------------------- +// Handle serial commands +void handle_serial() { + int c = Serial.read(); + if(c == 'P') { + Serial.println(F("Entering passthrough mode -- reset microcontroller to return to normal mode\n")); + passthrough = true; + } +} //-------------------------- Main loop ----------------------- void loop() { + if(passthrough) { + if(gpsPort.available()) Serial.write(gpsPort.read()); + if(Serial.available()) gpsPort.write(Serial.read()); + return; + } + if(Serial.available()) { + handle_serial(); + } while (gps.available( gpsPort )) { fix = gps.read(); if (fix.valid.location && fix.valid.date && fix.valid.time) From 0284dc50b579f680879d57423e7f080866ec6521 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 30 Jun 2018 22:15:06 -0500 Subject: [PATCH 2/6] Consistently use F-strings for serial prints --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index 5b8589a..dd8a6a1 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -15,7 +15,7 @@ SoftwareSerial gpsPort(2, 3); // RX, TX #define FIXOut 4 //Pin Out at IDC. Indicator for GPS Lock on pin 4 #define LDO_Enable A3 //GPS Voltage regulator Enable on pin A3 boolean GPSOK, passthrough; -const char softwareversion[] = "1.05" ; //Version of this program, sent to serialport at startup +#define softwareversion "1.05" //Version of this program, sent to serialport at startup NMEAGPS gps; // This parses the GPS characters gps_fix fix; // This holds on to the latest values @@ -27,9 +27,8 @@ void setup() { Serial.begin(9600); while (!Serial); - Serial.println(""); - Serial.print(F("Zachtek GPS referenced RF, Software version: ")); - Serial.println(softwareversion); + Serial.println(F("")); + Serial.println(F("Zachtek GPS referenced RF, Software version: " softwareversion)); pinMode(LDO_Enable, OUTPUT); // Set Voltage Regulator Enable pin as output. @@ -59,14 +58,14 @@ void setup() //Program GPS to output RF if (setGPS_OutputFreq1MHz()) { - Serial.println ("GPS Initialized to output RF at 1MHz"); - Serial.println ("Initialization is complete."); - Serial.println (""); + Serial.println (F("GPS Initialized to output RF at 1MHz")); + Serial.println (F("Initialization is complete.")); + Serial.println (F("")); GPSOK = true; } else { - Serial.println ("Error! Could not program GPS!"); + Serial.println (F("Error! Could not program GPS!")); GPSOK = false; } } @@ -134,8 +133,8 @@ bool setGPS_OutputFreq100kHz() }; sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); + bool gps_set_sucess = getUBX_ACK(setOutputFreq); + // Serial.println(F("Set output Freq Done")); return gps_set_sucess; } From 5f5ca8af1c26d468678104c3f0be1d9ef50bc883 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 30 Jun 2018 22:04:19 -0500 Subject: [PATCH 3/6] Support setting arbitrary frequencies --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 134 +++++++++++++----------------- 1 file changed, 56 insertions(+), 78 deletions(-) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index dd8a6a1..cf411ce 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -19,7 +19,18 @@ boolean GPSOK, passthrough; NMEAGPS gps; // This parses the GPS characters gps_fix fix; // This holds on to the latest values +constexpr auto MHz = 1000000lu; +constexpr auto KHz = 1000lu; +constexpr auto Hz = 1lu; +// This is a little bit arcane but it just lets you keep the user string +// and the actual value in synch automatically +#define SPEED 1 +#define UNITS MHz +#define STRINGIFY1(x) #x +#define STRINGIFY(x) STRINGIFY1(x) +#define SPEED_STR STRINGIFY(SPEED) STRINGIFY(UNITS) +#define SPEED_ARG (UINT32_C(SPEED) * UNITS) //-------------------------- SETUP ----------------------- @@ -57,10 +68,10 @@ void setup() delay(500);//Wait for GPSmodule to complete it's power on. //Program GPS to output RF - if (setGPS_OutputFreq1MHz()) { - Serial.println (F("GPS Initialized to output RF at 1MHz")); - Serial.println (F("Initialization is complete.")); - Serial.println (F("")); + if (setGPS_OutputFreq(SPEED_ARG)) { + Serial.println ("GPS Initialized to output RF at " SPEED_STR); + Serial.println ("Initialization is complete."); + Serial.println (""); GPSOK = true; } else @@ -123,81 +134,68 @@ void loop() //-------------------------- -bool setGPS_OutputFreq100kHz() -{ - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0xA0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x20, 0x1B - }; +// For details of the UBX protocol, see +// https://www.u-blox.com/sites/default/files/products/documents/u-blox7-V14_ReceiverDescriptionProtocolSpec_%28GPS.G7-SW-12001%29_Public.pdf +// Documentation of this packet is under the heading CFG-TP5 (35.19.2) in the current documentation. +uint8_t setOutputFreq[] = { +0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, +0x00, 0x00, 0xA0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, +0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x20, 0x1B +}; +#define OFFSET_FREQUENCY_LOCKED (18) +#define OFFSET_CKSUM (38) + +// Note: Normally payload_start is 2 bytes past the start of buf, because the first two bytes +// are the message type and are not checksummed. +void ubx_compute_checksum(uint8_t *payload_start, uint8_t *payload_end, uint8_t *cksum) { + uint8_t ck_a=0, ck_b=0; + for(const uint8_t *p = payload_start; p != payload_end; p++) + { + ck_a += *p; + ck_b += ck_a; + } + cksum[0] = ck_a; + cksum[1] = ck_b; +} + +bool setGPS_OutputFreq(uint32_t freq) { + for(int i=0; i<4; i++) { + setOutputFreq[OFFSET_FREQUENCY_LOCKED+i] = freq & 0xff; + freq >>= 8; + } + ubx_compute_checksum(setOutputFreq+2, setOutputFreq+38, setOutputFreq+38); sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); bool gps_set_sucess = getUBX_ACK(setOutputFreq); // Serial.println(F("Set output Freq Done")); return gps_set_sucess; } +bool setGPS_OutputFreq1Kz() +{ + return setGPS_OutputFreq(1*KHz); +} + bool setGPS_OutputFreq1MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x8A, 0x8B - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(1*MHz); } bool setGPS_OutputFreq2MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x80, 0x84, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x1B, 0x7F - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(2*MHz); } bool setGPS_OutputFreq4MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x3F, 0x8C - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(4*MHz); } //8MHz is the highest low-jitter frequency possible bool setGPS_OutputFreq8MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x12, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xD4, 0x28 - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(8*MHz); } //10 MHz is very jittery. Numbers that can be done with an integer division from 48MHz will produce @@ -205,34 +203,14 @@ bool setGPS_OutputFreq8MHz() //If 10MHz low jitter is needed then one option is to output 2MHz and then filter out the 5th overtone arriving at 10MHz in that way. bool setGPS_OutputFreq10MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 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, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xF6, 0x10 - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(10*MHz); } //16MHz is above the specs for lUblox Neo-6, only included for experiments. //This will not produce as clean Square wave. bool setGPS_OutputFreq16MHz() { - int gps_set_sucess = 0; - uint8_t setOutputFreq[] = { - 0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x24, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x60, 0x12 - }; - - sendUBX(setOutputFreq, sizeof(setOutputFreq) / sizeof(uint8_t)); - gps_set_sucess = getUBX_ACK(setOutputFreq); - //Serial.println("Set output Freq Done"); - return gps_set_sucess; + return setGPS_OutputFreq(16*MHz); } From c241ed14440350e5a86419b50015c3168fb13fb0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 Jul 2018 06:53:49 -0500 Subject: [PATCH 4/6] Use the same checksum computing code in ACK --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index cf411ce..f0f85d3 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -242,10 +242,7 @@ boolean getUBX_ACK(uint8_t *MSG) { 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]; - } + ubx_compute_checksum(ackPacket+2, ackPacket+8, ackPacket+8); while (1) { // Test for success if (ackByteID > 9) { From 16df1ad58468c8952452ae6d50e268c7694c384e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 Jul 2018 07:05:51 -0500 Subject: [PATCH 5/6] Support setting frequency via serial To set the frequency, connect via serial and enter e.g.,: F1M followed by enter/return to set the frequency to 1000000Hz (1MHz) Frequency can be set as a whole number, a whole number of kHz, or a whole number of MHz. The commands are case insensitive. So all the following set the frequency to 1MHz: F1000000 F1000k F1M No validation is performed and nonsensical things can be requested, so for example if you enter the nonsensical "F9MMM", you get "GPS Initialized to output RF at 3800301568" This specific number results from overflow of integer arithmetic and is also way out of spec for the board, since the actual value requested is around 3.8GHz. --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index f0f85d3..ef5cdec 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -15,6 +15,8 @@ SoftwareSerial gpsPort(2, 3); // RX, TX #define FIXOut 4 //Pin Out at IDC. Indicator for GPS Lock on pin 4 #define LDO_Enable A3 //GPS Voltage regulator Enable on pin A3 boolean GPSOK, passthrough; +int state; // serial command state machine +uint32_t freq; // Frequency in Hz commanded via serial #define softwareversion "1.05" //Version of this program, sent to serialport at startup NMEAGPS gps; // This parses the GPS characters gps_fix fix; // This holds on to the latest values @@ -91,6 +93,37 @@ void handle_serial() { Serial.println(F("Entering passthrough mode -- reset microcontroller to return to normal mode\n")); passthrough = true; } + else if(c == 'F' || c == 'f') { + Serial.print(F("Frequency?")); + state = 1; freq = 0; + } + else if(state == 1) { + if(c >= '0' && c <= '9') { + Serial.write(c); + freq = freq * 10 + c - '0'; + } else if (c == 'M' || c == 'm') { + Serial.write(c); + freq *= 1000000lu; + } else if (c == 'K' || c == 'k') { + Serial.write(c); + freq *= 1000lu; + } else if (c == '\n' || c == '\r') { + Serial.println(F("")); + if (setGPS_OutputFreq(freq)) { + Serial.print ("GPS Initialized to output RF at " ); + Serial.println (freq); + Serial.println ("Initialization is complete."); + Serial.println (""); + GPSOK = true; + } + else + { + Serial.println (F("Error! Could not program GPS!")); + GPSOK = false; + } + state = 0; + } + } } @@ -105,6 +138,7 @@ void loop() if(Serial.available()) { handle_serial(); } + if(state != 0) return; while (gps.available( gpsPort )) { fix = gps.read(); if (fix.valid.location && fix.valid.date && fix.valid.time) From c86e552d7fe00b0604d2669d3052e1926ed564b7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 6 Jul 2018 20:36:44 -0500 Subject: [PATCH 6/6] Allow a jumper to enable booting directly to passthrough mode Connect the SDA/SCL pins with a jumper or low value (e.g., 1K) resistor to enable passthrough mode. Disconnect the jumper to return to normal mode. These are pins 7/8 on the 10-pin connector, so they are convenient to short with a standard .100 jumper block. --- RFout1MHzV1_05/RFout1MHzV1_05.ino | 34 ++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/RFout1MHzV1_05/RFout1MHzV1_05.ino b/RFout1MHzV1_05/RFout1MHzV1_05.ino index ef5cdec..cd039ab 100644 --- a/RFout1MHzV1_05/RFout1MHzV1_05.ino +++ b/RFout1MHzV1_05/RFout1MHzV1_05.ino @@ -40,9 +40,19 @@ void setup() { Serial.begin(9600); while (!Serial); + Serial.println(F("")); Serial.println(F("Zachtek GPS referenced RF, Software version: " softwareversion)); + if(strapped_for_passthrough()) { + pinMode(LDO_Enable, OUTPUT); // Set Voltage Regulator Enable pin as output. + digitalWrite(LDO_Enable, HIGH); //Turn on 3.1V Power supply for the Ublox GPS module + gpsPort.begin(9600); + passthrough = 1; + Serial.println(F("Strapped for passthrough. Disconnect jumper between SDA/SCL for normal operation")); + return; + } + pinMode(LDO_Enable, OUTPUT); // Set Voltage Regulator Enable pin as output. //Blink the Lock led @@ -301,4 +311,26 @@ boolean getUBX_ACK(uint8_t *MSG) { }//else }//If }//While -}//getUBX_ACK +}//getUBX_ACK + +// If pins 7/8 on the 10-pin header are bridged, this is "strapped for passthrough" +// Detect this by driving one pin high and checking the other pin, then driving low and repeating +// set pins back to inputs before returning +bool strapped_for_passthrough() { + bool result = true; + pinMode(A4, OUTPUT); + + digitalWrite(A4, HIGH); + delay(1); + if(!digitalRead(A5)) result = false; + + digitalWrite(A4, LOW); + pinMode(A5, INPUT_PULLUP); + delay(1); + if(digitalRead(A5)) result = false; + + pinMode(A4, INPUT); + pinMode(A5, INPUT); + + return result; +}