2 * http://ad7zj.net/kd7lmo/aprsbeacon_code.html
4 * @mainpage Pico Beacon
6 * @section overview_sec Overview
8 * The Pico Beacon is an APRS based tracking beacon that operates in the UHF 420-450MHz band. The device utilizes a
9 * Microchip PIC 18F2525 embedded controller, Motorola M12+ GPS engine, and Analog Devices AD9954 DDS. The device is capable
10 * of generating a 1200bps A-FSK and 9600 bps FSK AX.25 compliant APRS (Automatic Position Reporting System) message.
14 * @section history_sec Revision History
16 * @subsection v305 V3.05
17 * 23 Dec 2006, Change include; (1) change printf format width to conform to ANSI standard when new CCS 4.xx compiler released.
20 * @subsection v304 V3.04
21 * 10 Jan 2006, Change include; (1) added amplitude control to engineering mode,
22 * (2) corrected number of bytes reported in log,
23 * (3) add engineering command to set high rate position reports (5 seconds), and
24 * (4) corrected size of LOG_COORD block when searching for end of log.
26 * @subsection v303 V3.03
27 * 15 Sep 2005, Change include; (1) removed AD9954 setting SDIO as input pin,
28 * (2) additional comments and Doxygen tags,
29 * (3) integration and test code calculates DDS FTW,
30 * (4) swapped bus and reference analog input ports (hardware change),
31 * (5) added message that indicates we are reading flash log and reports length,
32 * (6) report bus voltage in 10mV steps, and
33 * (7) change log type enumerated values to XORed nibbles for error detection.
36 * @subsection v302 V3.02
37 * 6 Apr 2005, Change include; (1) corrected tracked satellite count in NMEA-0183 $GPGGA message,
38 * (2) Doxygen documentation clean up and additions, and
39 * (3) added integration and test code to baseline.
42 * @subsection v301 V3.01
43 * 13 Jan 2005, Renamed project and files to Pico Beacon.
46 * @subsection v300 V3.00
47 * 15 Nov 2004, Change include; (1) Micro Beacon extreme hardware changes including integral transmitter,
48 * (2) PIC18F2525 processor,
49 * (3) AD9954 DDS support functions,
50 * (4) added comments and formatting for doxygen,
51 * (5) process GPS data with native Motorola protocol,
52 * (6) generate plain text $GPGGA and $GPRMC messages,
53 * (7) power down GPS 5 hours after lock,
54 * (8) added flight data recorder, and
55 * (9) added diagnostics terminal mode.
58 * @subsection v201 V2.01
59 * 30 Jan 2004, Change include; (1) General clean up of in-line documentation, and
60 * (2) changed temperature resolution to 0.1 degrees F.
63 * @subsection v200 V2.00
64 * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor,
66 * (3) GPS power control,
67 * (4) additional ADC input, and
68 * (5) LM60 temperature sensor.
71 * @subsection v101 V1.01
72 * 5 Dec 2001, Change include; (1) Changed startup message, and
73 * (2) applied SEPARATE pragma to several methods for memory usage.
76 * @subsection v100 V1.00
77 * 25 Sep 2001, Initial release. Flew ANSR-3 and ANSR-4.
83 * @section copyright_sec Copyright
85 * Copyright (c) 2001-2009 Michael Gray, KD7LMO
90 * @section gpl_sec GNU General Public License
92 * This program is free software; you can redistribute it and/or modify
93 * it under the terms of the GNU General Public License as published by
94 * the Free Software Foundation; either version 2 of the License, or
95 * (at your option) any later version.
97 * This program is distributed in the hope that it will be useful,
98 * but WITHOUT ANY WARRANTY; without even the implied warranty of
99 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100 * GNU General Public License for more details.
102 * You should have received a copy of the GNU General Public License
103 * along with this program; if not, write to the Free Software
104 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
110 * @section design Design Details
112 * Provides design details on a variety of the components that make up the Pico Beacon.
118 * @page power Power Consumption
120 * Measured DC power consumption.
122 * 3VDC prime power current
127 * 18mA Processor running, all I/O off
131 * 120mA GPS running w/antenna
133 * 250mA DDS running and GPS w/antenna
135 * 420mA DDS running, GPS w/antenna, and PA chain on with no RF
142 // Hardware specific configuration.
146 // NOTE: Even though we are using an external clock, we set the HS oscillator mode to
147 // make the PIC 18F252 work with our external clock which is a clipped 1V P-P sine wave.
148 #fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP
150 // C runtime library definitions.
154 // These compiler directives set the clock, SPI/I2C ports, and I/O configuration.
157 #use delay(clock=19200000)
159 // Engineering and data extracation port.
160 #use rs232(baud=57600, xmit=PIN_B7, rcv=PIN_B6, STREAM=PC_HOST)
163 #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
165 #use i2c (master, scl=PIN_C3, sda=PIN_C4)
171 // We define types that are used for all variables. These are declared
172 // because each processor has a different sizes for int and long.
173 // The PIC compiler defines int8_t, int16_t, and int32_t.
175 /// Boolean value { false, true }
176 typedef boolean bool_t;
178 /// Signed 8-bit number in the range -128 through 127.
179 typedef signed int8 int8_t;
181 /// Unsigned 8-bit number in the range 0 through 255.
182 typedef unsigned int8 uint8_t;
184 /// Signed 16-bit number in the range -32768 through 32767.
185 typedef signed int16 int16_t;
187 /// Unsigned 16-bit number in the range 0 through 65535.
188 typedef unsigned int16 uint16_t;
190 /// Signed 32-bit number in the range -2147483648 through 2147483647.
191 typedef signed int32 int32_t;
193 /// Unsigned 32-bit number in the range 0 through 4294967296.
194 typedef unsigned int32 uint32_t;
196 // Function and structure prototypes. These are declared at the start of
197 // the file much like a C++ header file.
199 // Map I/O pin names to hardware pins.
201 /// Heartbeat LED - Port A2
202 #define IO_LED PIN_A2
204 /// AD9954 DDS Profile Select 0 - Port A3
205 #define IO_PS0 PIN_A3
207 /// UHF amplifier and PA chain - Port A4
208 #define IO_PTT PIN_A4
210 /// AD9954 DDS Update - Port A5
211 #define IO_UPDATE PIN_A5
213 /// AD9954 CS (Chip Select) - Port B0
216 /// GPS Engine Power - Port B1
217 #define IO_GPS_PWR PIN_B1
219 /// AD9954 DDS Profile Select 1 - Port C0
220 #define IO_PS1 PIN_C0
222 /// AD9954 DDS OSK (Output Shift Key) - Port C2
223 #define IO_OSK PIN_C2
225 /// GPS engine serial transmit pin - Port C6
226 #define IO_GPS_TXD PIN_C6
228 // Public methods, constants, and data structures for each class.
230 /// Operational modes of the AD9954 DDS for the ddsSetMode function.
233 /// Device has not been initialized.
234 DDS_MODE_NOT_INITIALIZED,
236 /// Device in lowest power down mode.
239 /// Generate FM modulated audio tones.
242 /// Generate true FSK tones.
247 void ddsSetAmplitude (uint8_t amplitude);
248 void ddsSetOutputScale (uint16_t amplitude);
249 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
250 void ddsSetFreq (uint32_t freq);
251 void ddsSetFTW (uint32_t ftw);
252 void ddsSetMode (DDS_MODE mode);
255 uint8_t flashGetByte ();
256 void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length);
257 void flashSendByte(uint8_t value);
258 void flashSendAddress(uint32_t address);
259 void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length);
267 /// 2D (Latitude/Longitude) fix.
270 /// 3D (Latitude/Longitude/Altitude) fix.
274 /// GPS Position information.
277 /// Flag that indicates the position information has been updated since it was last checked.
280 /// Month in UTC time.
283 /// Day of month in UTC time.
286 /// Hours in UTC time.
289 /// Minutes in UTC time.
292 /// Seconds in UTC time.
295 /// Year in UTC time.
298 /// Latitude in milli arc-seconds where + is North, - is South.
301 /// Longitude in milli arc-seconds where + is East, - is West.
307 /// Calculated altitude in feet
308 int32_t altitudeFeet;
310 /// 3D speed in cm/second.
313 /// 2D speed in cm/second.
316 /// Heading units of 0.1 degrees.
319 /// DOP (Dilution of Precision)
322 /// 16-bit number that represents status of GPS engine.
325 /// Number of tracked satellites used in the fix position.
328 /// Number of visible satellites.
330 } GPSPOSITION_STRUCT;
334 GPS_FIX_TYPE gpsGetFixType();
335 int32_t gpsGetPeakAltitude();
340 int16_t lm92GetTemp();
342 /// Define the log record types.
345 /// Time stamp the log was started.
352 LOG_TEMPERATURE = 0x96,
359 uint32_t logGetAddress();
360 void logType (LOG_TYPE type);
361 void logUint8 (uint8_t value);
362 void logInt16 (int16_t value);
364 bool_t serialHasData();
366 uint8_t serialRead();
369 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
371 void sysLogVoltage();
373 /// 0% duty cycle (LED Off) constant for function timeSetDutyCycle
374 #define TIME_DUTYCYCLE_0 0
376 /// 10% duty cycle constant for function timeSetDutyCycle
377 #define TIME_DUTYCYCLE_10 1
379 /// 70% duty cycle constant for function timeSetDutyCycle
380 #define TIME_DUTYCYCLE_70 7
382 uint8_t timeGetTicks();
384 void timeSetDutyCycle (uint8_t dutyCycle);
387 /// Operational modes of the TNC for the tncSetMode function.
390 /// No operation waiting for setup and configuration.
393 /// 1200 bps using A-FSK (Audio FSK) tones.
396 /// 9600 bps using true FSK tones.
402 void tncHighRate(bool_t state);
403 void tncSetMode (TNC_DATA_MODE dataMode);
404 void tnc1200TimerTick();
405 void tnc9600TimerTick();
406 void tncTxByte (uint8_t value);
407 void tncTxPacket(TNC_DATA_MODE dataMode);
410 * @defgroup ADC Analog To Digital Converter
412 * Control and manage the on board PIC A/D converter.
417 /// Filtered voltages using a single pole, low pass filter.
418 uint16_t adcMainBusVolt;
420 /// PIC ADC Channel number of the reference voltage.
423 /// PIC ADC Channel number of the main bus voltage.
424 #define ADC_MAINBUS 1
426 /// Input diode drop in units of 0.01 volts.
427 #define MAIN_BUS_VOLT_OFFSET 20
430 * Intialize the ADC subsystem.
435 setup_adc_ports(AN0_TO_AN1);
436 setup_adc( ADC_CLOCK_DIV_32 );
438 // Zero the ADC filters.
443 * Filtered main bus voltage in 10mV resolution.
445 * @return voltage in 10mV steps
447 uint16_t adcGetMainBusVolt()
451 volts = (uint32_t) (adcMainBusVolt >> 3);
453 volts = (volts * 330l) / 1023l;
455 return (uint16_t) volts + MAIN_BUS_VOLT_OFFSET;
459 * Get the current ADC value for the main bus voltage.
461 * @return ADC value in the range 0 to 1023
463 uint16_t adcRawBusVolt()
465 set_adc_channel(ADC_MAINBUS);
471 * Get the current ADC value for the reference source voltage.
473 * @return ADC value in the range 0 to 1023
475 uint16_t adcRawRefVolt()
477 set_adc_channel(ADC_REF);
483 * Read and filter the ADC channels for bus voltages.
487 // Filter the bus voltage using a single pole low pass filter.
488 set_adc_channel(ADC_MAINBUS);
490 adcMainBusVolt = read_adc() + adcMainBusVolt - (adcMainBusVolt >> 3);
497 * @defgroup diag Diagnostics and Control
499 * Functions for diagnostics and control of the hardware and flight data recorder.
504 /// Number of bytes per line to display when reading flight data recorder.
505 #define DIAG_BYTES_PER_LINE 32
508 * Process the command to erase the data logger flash.
510 void diagEraseFlash()
512 // Confirm we want to erase the flash with the key sequence 'yes' .
513 fprintf (PC_HOST, "Are you sure (yes)? ");
515 if (fgetc(PC_HOST) != 'y')
518 if (fgetc(PC_HOST) != 'e')
521 if (fgetc(PC_HOST) != 's')
524 if (fgetc(PC_HOST) != 13)
527 // User feedback and erase the part.
528 fprintf (PC_HOST, "Erasing flash...");
532 fprintf (PC_HOST, "done.\n\r");
536 * Display the engineering mode menu.
541 fprintf (PC_HOST, "Options: (e)rase Flash, (r)ead Flash\n\r");
542 fprintf (PC_HOST, " Toggle (L)ED\n\r");
543 fprintf (PC_HOST, " (P)TT - Push To Transmit\n\r");
544 fprintf (PC_HOST, " (f)requencey down, (F)requency up - 1KHz step\n\r");
545 fprintf (PC_HOST, " (c)hannel down, (C)hannel up - 25KHz step\n\r");
546 fprintf (PC_HOST, " (a)mplitude down, (A)mplitude up - 0.5 dB steps\n\r");
547 fprintf (PC_HOST, " e(x)it engineering mode\n\r");
551 * Process the command to dump the contents of the data logger flash.
555 bool_t dataFoundFlag, userStopFlag;
556 uint8_t i, buffer[DIAG_BYTES_PER_LINE];
559 // Set the initial conditions to read the flash.
561 userStopFlag = false;
565 // Read each block from the flash device.
566 flashReadBlock (address, buffer, DIAG_BYTES_PER_LINE);
568 // This flag will get set if any data byte is not equal to 0xff (erase flash state)
569 dataFoundFlag = false;
571 // Display the address.
572 fprintf (PC_HOST, "%08lx ", address);
574 // Display each byte in the line.
575 for (i = 0; i < DIAG_BYTES_PER_LINE; ++i)
577 fprintf (PC_HOST, "%02x", buffer[i]);
579 // Set this flag if the cell is not erased.
580 if (buffer[i] != 0xff)
581 dataFoundFlag = true;
583 // Any key will abort the transfer.
588 // at the end of each line.
589 fprintf (PC_HOST, "\n\r");
591 // Advance to the next block of memory.
592 address += DIAG_BYTES_PER_LINE;
593 } while (dataFoundFlag && !userStopFlag);
595 // Feedback to let the user know why the transfer stopped.
597 fprintf (PC_HOST, "User aborted download!\n\r");
602 uint16_t timeStamp, lastTimeStamp;
604 lastTimeStamp = 0x0000;
612 if (timeStamp != lastTimeStamp)
618 fprintf (PC_HOST, "%lu %lu\n\r", timeStamp, (timeStamp - lastTimeStamp));
620 lastTimeStamp = timeStamp;
626 * Process diagnostic commands through the debug RS-232 port.
630 bool_t diagDoneFlag, ledFlag, paFlag, showSettingsFlag;
631 uint8_t command, amplitude;
634 // If the input is low, we aren't connected to the RS-232 device so continue to boot.
638 fprintf (PC_HOST, "Engineering Mode\n\r");
639 fprintf (PC_HOST, "Application Built %s %s\n\r", __DATE__, __TIME__);
641 // Current state of the status LED.
643 output_bit (IO_LED, ledFlag);
645 // This flag indicates we are ready to leave the diagnostics mode.
646 diagDoneFlag = false;
648 // Current state of the PA.
651 // Flag that indicate we should show the current carrier frequency.
652 showSettingsFlag = false;
654 // Set the initial carrier frequency and amplitude.
658 // Wait for the exit command.
659 while (!diagDoneFlag)
661 // Wait for the user command.
662 command = fgetc(PC_HOST);
664 // Decode and process the key stroke.
674 ledFlag = (ledFlag ? false : true);
675 output_bit (IO_LED, ledFlag);
690 fprintf (PC_HOST, "Set high rate TNC.\n\r");
697 // Display the new frequency.
698 showSettingsFlag = true;
705 // Display the new frequency.
706 showSettingsFlag = true;
713 // Display the new frequency.
714 showSettingsFlag = true;
721 // Display the new frequency.
722 showSettingsFlag = true;
729 paFlag = (paFlag ? false : true);
730 output_bit (IO_PTT, paFlag);
731 output_bit (IO_OSK, paFlag);
735 ddsSetMode (DDS_MODE_AFSK);
736 ddsSetAmplitude (amplitude);
738 ddsSetMode (DDS_MODE_POWERDOWN);
743 if (amplitude != 200)
746 ddsSetAmplitude (amplitude);
748 // Display the new amplitude.
749 showSettingsFlag = true;
757 ddsSetAmplitude (amplitude);
759 // Display the new amplitude.
760 showSettingsFlag = true;
773 fprintf (PC_HOST, "Invalid command. (H)elp for menu.\n\r");
777 // Display the results of any user requests or commands.
778 if (showSettingsFlag)
780 showSettingsFlag = false;
782 fprintf (PC_HOST, "%03ld.%03ld MHz ", freqHz / 1000000, (freqHz / 1000) % 1000);
783 fprintf (PC_HOST, "%d.%01ddBc\n\r", amplitude / 10, amplitude % 10);
789 // Let the user know we are done with this mode.
790 fprintf (PC_HOST, "Exit diagnostic mode.\n\r");
799 * @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
801 * Functions to control the Analog Devices AD9954 DDS.
806 /// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings.
807 #define DDS_AD9954_CFR1 0x00
809 /// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current.
810 #define DDS_AD9954_CFR2 0x01
812 /// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff).
813 #define DDS_AD9954_ASF 0x02
815 /// AD9954 ARR - Amplitude ramp rate for OSK function.
816 #define DDS_AD9954_ARR 0x03
818 /// AD9954 FTW0 - Frequency tuning word 0.
819 #define DDS_AD9954_FTW0 0x04
821 /// AD9954 FTW1 - Frequency tuning word 1
822 #define DDS_AD9954_FTW1 0x06
824 /// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode
825 #define DDS_AD9954_NLSCW 0x07
827 /// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode
828 #define DDS_AD9954_PLSCW 0x08
830 /// AD9954 RSCW0 - RAM Segment Control Word 0
831 #define DDS_AD9954_RWCW0 0x07
833 /// AD9954 RSCW0 - RAM Segment Control Word 1
834 #define DDS_AD9954_RWCW1 0x08
836 /// AD9954 RAM segment
839 /// Current operational mode.
842 /// Number of digits in DDS frequency to FTW conversion.
843 #define DDS_FREQ_TO_FTW_DIGITS 9
845 /// Array of multiplication factors used to convert frequency to the FTW.
846 const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
848 /// Array of divisors used to convert frequency to the FTW.
849 const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
851 /// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
852 const uint16_t DDS_AMP_TO_SCALE[] =
854 16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697,
855 8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463,
856 3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638
860 /// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz (445.950MHz)
861 const uint32_t freqTable[256] =
863 955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351,
864 955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249,
865 955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495,
866 955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773,
867 955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803,
868 955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354,
869 955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250,
870 955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380,
871 955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701,
872 955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238,
873 955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088,
874 955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408,
875 955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417,
876 955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385,
877 955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618,
878 955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456,
879 955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249,
880 955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351,
881 955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105,
882 955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827,
883 955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797,
884 955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246,
885 955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350,
886 955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220,
887 955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900,
888 955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362,
889 955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513,
890 955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192,
891 955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183,
892 955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216,
893 955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982,
894 955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
898 * Initialize the DDS regsiters and RAM.
902 // Setup the SPI port for the DDS interface.
903 setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H );
905 // Set the initial DDS mode. The ddsSetMode function uses this value to make the desired DDS selections.
906 ddsMode = DDS_MODE_NOT_INITIALIZED;
908 // Set the DDS operational mode.
909 ddsSetMode (DDS_MODE_POWERDOWN);
911 // Set the output to full scale.
912 ddsSetOutputScale (0x3fff);
914 // CFR2 (Control Function Register No. 2)
916 spi_write (DDS_AD9954_CFR2);
918 spi_write (0x00); // Unused register bits
920 spi_write (0x9c); // 19x reference clock multipler, high VCO range, nominal charge pump current
923 // ARR (Amplitude Ramp Rate) to 15mS for OSK
925 spi_write (DDS_AD9954_ARR);
930 // Strobe the part so we apply the updates.
931 output_high (IO_UPDATE);
932 output_low (IO_UPDATE);
936 * Set DDS amplitude value in the range 0 to 16383 where 16383 is full scale. This value is a
937 * linear multiplier and needs to be scale for RF output power in log scale.
939 * @param scale in the range 0 to 16383
941 void ddsSetOutputScale (uint16_t scale)
943 // Set ASF (Amplitude Scale Factor)
945 spi_write (DDS_AD9954_ASF);
947 spi_write ((scale >> 8) & 0xff);
948 spi_write (scale & 0xff);
952 // Strobe the DDS to set the amplitude.
953 output_high (IO_UPDATE);
954 output_low (IO_UPDATE);
958 * Set the DDS amplitude in units of dBc of full scale where 1 is 0.1 dB. For example, a value of 30 is 3dBc
959 * or a value of 85 is 8.5dBc.
961 * @param amplitude in 0.1 dBc of full scale
963 void ddsSetAmplitude (uint8_t amplitude)
965 // Range limit based on the lookup table size.
969 // Set the linear DDS ASF (Amplitude Scale Factor) based on the dB lookup table.
970 ddsSetOutputScale (DDS_AMP_TO_SCALE[amplitude / 5]);
972 // Toggle the DDS output low and then high to force it to ramp to the new output level setting.
976 output_high (IO_OSK);
981 * Set DDS frequency tuning word. The output frequency is equal to RefClock * (ftw / 2 ^ 32).
983 * @param ftw Frequency Tuning Word
985 void ddsSetFTW (uint32_t ftw)
987 // Set FTW0 (Frequency Tuning Word 0)
989 spi_write (DDS_AD9954_FTW0);
991 spi_write ((ftw >> 24) & 0xff);
992 spi_write ((ftw >> 16) & 0xff);
993 spi_write ((ftw >> 8) & 0xff);
994 spi_write (ftw & 0xff);
998 // Strobe the DDS to set the frequency.
999 output_high (IO_UPDATE);
1000 output_low (IO_UPDATE);
1004 * Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word).
1006 * @param freq frequency in Hertz
1009 void ddsSetFreq(uint32_t freq)
1014 // To avoid rounding errors with floating point math, we do a long multiply on the data.
1015 ftw = freq * DDS_MULT[0];
1017 for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i)
1018 ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i];
1024 * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal
1025 * to RefClock * (ftw / 2 ^ 32).
1027 * @param ftw0 frequency tuning word for the FSK 0 value
1028 * @param ftw1 frequency tuning word for the FSK 1 value
1030 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1)
1032 // Set FTW0 (Frequency Tuning Word 0)
1034 spi_write (DDS_AD9954_FTW0);
1036 spi_write ((ftw0 >> 24) & 0xff);
1037 spi_write ((ftw0 >> 16) & 0xff);
1038 spi_write ((ftw0 >> 8) & 0xff);
1039 spi_write (ftw0 & 0xff);
1041 output_high (IO_CS);
1043 // Set FTW0 (Frequency Tuning Word 1)
1045 spi_write (DDS_AD9954_FTW1);
1047 spi_write ((ftw1 >> 24) & 0xff);
1048 spi_write ((ftw1 >> 16) & 0xff);
1049 spi_write ((ftw1 >> 8) & 0xff);
1050 spi_write (ftw1 & 0xff);
1052 output_high (IO_CS);
1054 // Strobe the DDS to set the frequency.
1055 output_high (IO_UPDATE);
1056 output_low (IO_UPDATE);
1060 * Set the DDS to run in A-FSK, FSK, or PSK31 mode
1062 * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant
1064 void ddsSetMode (DDS_MODE mode)
1066 // Save the current mode.
1071 case DDS_MODE_POWERDOWN:
1072 // CFR1 (Control Function Register No. 1)
1074 spi_write (DDS_AD9954_CFR1);
1079 spi_write (0xf0); // Power down all subsystems.
1080 output_high (IO_CS);
1084 // CFR1 (Control Function Register No. 1)
1086 spi_write (DDS_AD9954_CFR1);
1088 spi_write (0x03); // OSK Enable and Auto OSK keying
1091 spi_write (0x40); // Power down comparator circuit
1092 output_high (IO_CS);
1096 // CFR1 (Control Function Register No. 1)
1098 spi_write (DDS_AD9954_CFR1);
1100 spi_write (0x03); // Clear RAM Enable, OSK Enable, Auto OSK keying
1103 spi_write (0x40); // Power down comparator circuit
1104 output_high (IO_CS);
1106 // NOTE: The sweep rate requires 1/4 of a bit time (26uS) to transition.
1107 // 6KHz delta = 70641 counts = (6KHz / 364.8MHz) * 2 ^ 32
1108 // SYNC_CLK = 91.2MHz 1/91.2MHz * 70641 * 1/29 = 26.7uS
1110 // NLSCW (Negative Linear Sweep Control Word)
1112 spi_write (DDS_AD9954_NLSCW);
1114 spi_write (1); // Falling sweep ramp rate word
1115 spi_write (0x00); // Delta frequency tuning word
1119 output_high (IO_CS);
1121 // PLSCW (Positive Linear Sweep Control Word)
1123 spi_write (DDS_AD9954_PLSCW);
1125 spi_write (1); // Rising sweep ramp rate word
1126 spi_write (0x00); // Delta frequency tuning word
1130 output_high (IO_CS);
1134 // Strobe the DDS to change the mode.
1135 output_high (IO_UPDATE);
1136 output_low (IO_UPDATE);
1142 * @defgroup flash Flash Manager
1144 * Functions to control the ST MP25P80 serial flash device.
1149 /// Flash Chip Select - Port B3
1150 #define FLASH_CS PIN_B3
1152 /// Flash Clock - Port B5
1153 #define FLASH_CLK PIN_B5
1155 /// Flash Data Input - Port B4
1156 #define FLASH_D PIN_B4
1158 /// Flash Data Output - Port B2
1159 #define FLASH_Q PIN_B2
1162 * Determine if a flash write or erase operation is currently in progress.
1164 * @return true if write/erase in progress
1166 bool_t flashIsWriteInProgress()
1170 output_low (FLASH_CS);
1172 // Read Status Register (RDSR) flash command.
1173 flashSendByte (0x05);
1175 status = flashGetByte();
1177 output_high (FLASH_CS);
1179 return (((status & 0x01) == 0x01) ? true : false);
1183 * Read a block of memory from the flash device.
1185 * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
1186 * @param block pointer to locate of data block
1187 * @param length number of bytes to read
1189 void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length)
1193 output_low (FLASH_CS);
1195 // Read Data Byte(s) (READ) flash command.
1196 flashSendByte (0x03);
1197 flashSendAddress (address);
1199 for (i = 0; i < length; ++i)
1200 *block++ = flashGetByte();
1202 output_high (FLASH_CS);
1206 * Write a block of memory to the flash device.
1208 * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
1209 * @param block pointer data block to write
1210 * @param length number of bytes to write
1212 void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length)
1216 output_low (FLASH_CS);
1217 // Write Enable (WREN) flash command.
1218 flashSendByte (0x06);
1219 output_high (FLASH_CS);
1221 output_low (FLASH_CS);
1222 // Page Program (PP) flash command.
1223 flashSendByte (0x02);
1224 flashSendAddress (address);
1226 for (i = 0; i < length; ++i)
1228 // Send each byte in the data block.
1229 flashSendByte (*block++);
1231 // Track the address in the flash device.
1234 // If we cross a page boundary (a page is 256 bytes) we need to stop and send the address again.
1235 if ((address & 0xff) == 0x00)
1237 output_high (FLASH_CS);
1239 // Write this block of data.
1240 while (flashIsWriteInProgress());
1242 output_low (FLASH_CS);
1243 // Write Enable (WREN) flash command.
1244 flashSendByte (0x06);
1245 output_high (FLASH_CS);
1247 output_low (FLASH_CS);
1248 // Page Program (PP) flash command.
1249 flashSendByte (0x02);
1250 flashSendAddress (address);
1254 output_high (FLASH_CS);
1256 // Wait for the final write operation to complete.
1257 while (flashIsWriteInProgress());
1261 * Erase the entire flash device (all locations set to 0xff).
1265 output_low (FLASH_CS);
1266 // Write Enable (WREN) flash command.
1267 flashSendByte (0x06);
1268 output_high (FLASH_CS);
1270 output_low (FLASH_CS);
1271 // Bulk Erase (BE) flash command.
1272 flashSendByte (0xc7);
1273 output_high (FLASH_CS);
1275 while (flashIsWriteInProgress());
1279 * Read a single byte from the flash device through the serial interface. This function
1280 * only controls the clock line. The chip select must be configured before calling
1283 * @return byte read from device
1285 uint8_t flashGetByte()
1291 // Bit bang the 8-bits.
1292 for (i = 0; i < 8; ++i)
1294 // Data is ready on the rising edge of the clock.
1295 output_high (FLASH_CLK);
1297 // MSB is first, so shift left.
1300 if (input (FLASH_Q))
1301 value = value | 0x01;
1303 output_low (FLASH_CLK);
1310 * Initialize the flash memory subsystem.
1314 // I/O lines to control flash.
1315 output_high (FLASH_CS);
1316 output_low (FLASH_CLK);
1317 output_low (FLASH_D);
1321 * Write a single byte to the flash device through the serial interface. This function
1322 * only controls the clock line. The chip select must be configured before calling
1325 * @param value byte to write to device
1327 void flashSendByte(uint8_t value)
1331 // Bit bang the 8-bits.
1332 for (i = 0; i < 8; ++i)
1334 // Drive the data input pin.
1335 if ((value & 0x80) == 0x80)
1336 output_high (FLASH_D);
1338 output_low (FLASH_D);
1340 // MSB is first, so shift leeft.
1343 // Data is accepted on the rising edge of the clock.
1344 output_high (FLASH_CLK);
1345 output_low (FLASH_CLK);
1350 * Write the 24-bit address to the flash device through the serial interface. This function
1351 * only controls the clock line. The chip select must be configured before calling
1354 * @param address 24-bit flash device address
1356 void flashSendAddress(uint32_t address)
1360 // Bit bang the 24-bits.
1361 for (i = 0; i < 24; ++i)
1363 // Drive the data input pin.
1364 if ((address & 0x800000) == 0x800000)
1365 output_high (FLASH_D);
1367 output_low (FLASH_D);
1369 // MSB is first, so shift left.
1370 address = address << 1;
1372 // Data is accepted on the rising edge of the clock.
1373 output_high (FLASH_CLK);
1374 output_low (FLASH_CLK);
1381 * @defgroup GPS Motorola M12+ GPS Engine
1383 * Functions to control the Motorola M12+ GPS engine in native binary protocol mode.
1388 /// The maximum length of a binary GPS engine message.
1389 #define GPS_BUFFER_SIZE 50
1391 /// GPS parse engine state machine values.
1392 enum GPS_PARSE_STATE_MACHINE
1394 /// 1st start character '@'
1397 /// 2nd start character '@'
1400 /// Upper case 'A' - 'Z' message type
1403 /// Lower case 'a' - 'z' message type
1406 /// 0 - xx bytes based on message type 'Aa'
1410 GPS_CHECKSUMMESSAGE,
1412 /// End of message - Carriage Return
1415 /// End of message - Line Feed
1419 /// Index into gpsBuffer used to store message data.
1422 /// State machine used to parse the GPS message stream.
1423 GPS_PARSE_STATE_MACHINE gpsParseState;
1425 /// Buffer to store data as it is read from the GPS engine.
1426 uint8_t gpsBuffer[GPS_BUFFER_SIZE];
1428 /// Peak altitude detected while GPS is in 3D fix mode.
1429 int32_t gpsPeakAltitude;
1431 /// Checksum used to verify binary message from GPS engine.
1432 uint8_t gpsChecksum;
1434 /// Last verified GPS message received.
1435 GPSPOSITION_STRUCT gpsPosition;
1438 * Get the type of fix.
1440 * @return gps fix type enumeration
1442 GPS_FIX_TYPE gpsGetFixType()
1444 // The upper 3-bits determine the fix type.
1445 switch (gpsPosition.status & 0xe000)
1459 * Peak altitude detected while GPS is in 3D fix mode since the system was booted.
1461 * @return altitude in feet
1463 int32_t gpsGetPeakAltitude()
1465 return gpsPeakAltitude;
1469 * Initialize the GPS subsystem.
1473 // Initial parse state.
1474 gpsParseState = GPS_START1;
1476 // Assume we start at sea level.
1477 gpsPeakAltitude = 0;
1479 // Clear the structure that stores the position message.
1480 memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT));
1482 // Setup the timers used to measure the 1-PPS time period.
1483 setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
1484 setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3);
1488 * Determine if new GPS message is ready to process. This function is a one shot and
1489 * typically returns true once a second for each GPS position fix.
1491 * @return true if new message available; otherwise false
1495 if (gpsPosition.updateFlag)
1497 gpsPosition.updateFlag = false;
1505 * Calculate NMEA-0183 message checksum of buffer that is length bytes long.
1507 * @param buffer pointer to data buffer.
1508 * @param length number of bytes in buffer.
1510 * @return checksum of buffer
1512 uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length)
1514 uint8_t i, checksum;
1518 for (i = 0; i < length; ++i)
1519 checksum ^= buffer[i];
1525 * Verify the GPS engine is sending the @@Hb position report message. If not,
1526 * configure the GPS engine to send the desired report.
1528 * @return true if GPS engine operation; otherwise false
1532 uint8_t startTime, retryCount;
1534 // We wait 10 seconds for the GPS engine to respond to our message request.
1535 startTime = timeGetTicks();
1538 while (++retryCount < 10)
1540 // Read the serial FIFO and process the GPS messages.
1543 // If a GPS data set is available, then GPS is operational.
1546 timeSetDutyCycle (TIME_DUTYCYCLE_10);
1550 if (timeGetTicks() > startTime)
1552 puts ("@@Hb\001\053\015\012");
1562 * Parse the Motorola @@Hb (Short position/message) report.
1564 void gpsParsePositionMessage()
1566 // Convert the binary stream into data elements. We will scale to the desired units
1567 // as the values are used.
1568 gpsPosition.updateFlag = true;
1570 gpsPosition.month = gpsBuffer[0];
1571 gpsPosition.day = gpsBuffer[1];
1572 gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3];
1573 gpsPosition.hours = gpsBuffer[4];
1574 gpsPosition.minutes = gpsBuffer[5];
1575 gpsPosition.seconds = gpsBuffer[6];
1576 gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14];
1577 gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18];
1578 gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22];
1579 gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l;
1580 gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28];
1581 gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30];
1582 gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32];
1583 gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34];
1584 gpsPosition.visibleSats = gpsBuffer[35];
1585 gpsPosition.trackedSats = gpsBuffer[36];
1586 gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38];
1588 // Update the peak altitude if we have a valid 3D fix.
1589 if (gpsGetFixType() == GPS_3D_FIX)
1590 if (gpsPosition.altitudeFeet > gpsPeakAltitude)
1591 gpsPeakAltitude = gpsPosition.altitudeFeet;
1595 * Turn on the GPS engine power and serial interface.
1599 // 3.0 VDC LDO control line.
1600 output_high (IO_GPS_PWR);
1602 // Enable the UART and the transmit line.
1609 * Turn off the GPS engine power and serial interface.
1613 // Disable the UART and the transmit line.
1618 // 3.0 VDC LDO control line.
1619 output_low (IO_GPS_PWR);
1623 * Read the serial FIFO and process complete GPS messages.
1629 // This state machine handles each characters as it is read from the GPS serial port.
1630 // We are looking for the GPS mesage @@Hb ... C
1631 while (serialHasData())
1633 // Get the character value.
1634 value = serialRead();
1636 // Process based on the state machine.
1637 switch (gpsParseState)
1641 gpsParseState = GPS_START2;
1646 gpsParseState = GPS_COMMAND1;
1648 gpsParseState = GPS_START1;
1653 gpsParseState = GPS_COMMAND2;
1655 gpsParseState = GPS_START1;
1661 gpsParseState = GPS_READMESSAGE;
1667 gpsParseState = GPS_START1;
1670 case GPS_READMESSAGE:
1671 gpsChecksum ^= value;
1672 gpsBuffer[gpsIndex++] = value;
1675 gpsParseState = GPS_CHECKSUMMESSAGE;
1679 case GPS_CHECKSUMMESSAGE:
1680 if (gpsChecksum == value)
1681 gpsParseState = GPS_EOMCR;
1683 gpsParseState = GPS_START1;
1688 gpsParseState = GPS_EOMLF;
1690 gpsParseState = GPS_START1;
1694 // Once we have the last character, convert the binary message to something usable.
1696 gpsParsePositionMessage();
1698 gpsParseState = GPS_START1;
1708 * @defgroup log Flight Data Recorder
1710 * Functions to manage and control the flight data recorder
1715 /// Number of bytes to buffer before writing to flash memory.
1716 #define LOG_WRITE_BUFFER_SIZE 40
1718 /// Last used address in flash memory.
1719 uint32_t logAddress;
1721 /// Temporary buffer that holds data before it is written to flash device.
1722 uint8_t logBuffer[LOG_WRITE_BUFFER_SIZE];
1724 /// Current index into log buffer.
1728 * Last used address in flash memory. This location is where the next log data will
1731 * @return 24-bit flash memory address
1733 uint32_t logGetAddress()
1739 * Write the contents of the temporary log buffer to the flash device. If the buffer
1740 * is empty, nothing is done.
1744 // We only need to write if there is data.
1747 flashWriteBlock (logAddress, logBuffer, logIndex);
1748 logAddress += logIndex;
1754 * Prepare the flight data recorder for logging.
1761 fprintf (PC_HOST, "Searching for end of flash log...");
1763 logAddress = 0x0000;
1766 // Read each logged data block from flash to determine how long it is.
1769 // Read the data log entry type.
1770 flashReadBlock (logAddress, buffer, 1);
1772 // Based on the log entry type, we'll skip over the data contained in the entry.
1783 case LOG_TEMPERATURE:
1798 } while (logAddress < 0x100000 && !endFound);
1800 fprintf (PC_HOST, "done. Log contains %ld bytes.\n\r", logAddress);
1806 * Start a entry in the data log.
1808 * @param type of log entry, i.e. LOG_BOOTED, LOG_COORD, etc.
1810 void logType (LOG_TYPE type)
1812 // Only add the new entry if there is space.
1813 if (logAddress >= 0x100000)
1816 // Write the old entry first.
1819 // Save the type and set the log buffer pointer.
1820 logBuffer[0] = type;
1825 * Save an unsigned, 8-bit value in the log.
1827 * @param value unsigned, 8-bit value
1829 void logUint8 (uint8_t value)
1831 logBuffer[logIndex++] = value;
1835 * Save a signed, 16-bit value in the log.
1837 * @param value signed, 16-bit value
1839 void logInt16 (int16_t value)
1841 logBuffer[logIndex++] = (value >> 8) & 0xff;
1842 logBuffer[logIndex++] = value & 0xff;
1846 * Save an unsigned, 16-bit value in the log.
1848 * @param value unsigned, 16-bit value
1850 void logUint16 (uint16_t value)
1852 logBuffer[logIndex++] = (value >> 8) & 0xff;
1853 logBuffer[logIndex++] = value & 0xff;
1857 * Save a signed, 32-bit value in the log.
1859 * @param value signed, 32-bit value
1861 void logInt32 (int32_t value)
1863 logBuffer[logIndex++] = (value >> 24) & 0xff;
1864 logBuffer[logIndex++] = (value >> 16) & 0xff;
1865 logBuffer[logIndex++] = (value >> 8) & 0xff;
1866 logBuffer[logIndex++] = value & 0xff;
1872 * @defgroup LM92 LM92 temperature sensor
1874 * Read and control the National Semiconductor LM92 I2C temperature sensor
1880 * Read the LM92 temperature value in 0.1 degrees F.
1882 * @return 0.1 degrees F
1884 int16_t lm92GetTemp()
1889 // Set the SDA and SCL to input pins to control the LM92.
1892 // Read the temperature register value.
1895 value = ((int16_t) i2c_read() << 8);
1896 value = value | ((int16_t) i2c_read() & 0x00f8);
1899 // Set the SDA and SCL back to outputs for use with the AD9954 because we share common clock pins.
1902 // LM92 register 0.0625degC/bit 9 10 9
1903 // ------------- * -------------- * - * -- = -- + 320
1906 // Convert to degrees F.
1907 temp = (int32_t) value;
1908 temp = ((temp * 9l) / 64l) + 320;
1910 return (int16_t) temp;
1917 * @defgroup serial Serial Port FIFO
1919 * FIFO for the built-in serial port.
1924 /// Size of serial port FIFO in bytes. It must be a power of 2, i.e. 2, 4, 8, 16, etc.
1925 #define SERIAL_BUFFER_SIZE 64
1927 /// Mask to wrap around at end of circular buffer. (SERIAL_BUFFER_SIZE - 1)
1928 #define SERIAL_BUFFER_MASK 0x3f
1930 /// Index to the next free location in the buffer.
1933 /// Index to the next oldest data in the buffer.
1936 /// Circular buffer (FIFO) to hold serial data.
1937 uint8_t serialBuffer[SERIAL_BUFFER_SIZE];
1940 * Determine if the FIFO contains data.
1942 * @return true if data present; otherwise false
1944 bool_t serialHasData()
1946 if (serialHead == serialTail)
1953 * Initialize the serial processor.
1962 * Get the oldest character from the FIFO.
1964 * @return oldest character; 0 if FIFO is empty
1966 uint8_t serialRead()
1970 // Make sure we have something to return.
1971 if (serialHead == serialTail)
1975 value = serialBuffer[serialTail];
1977 // Update the pointer.
1978 serialTail = (serialTail + 1) & SERIAL_BUFFER_MASK;
1984 * Read and store any characters in the PIC serial port in a FIFO.
1988 // If there isn't a character in the PIC buffer, just leave.
1991 // Save the value in the FIFO.
1992 serialBuffer[serialHead] = getc();
1994 // Move the pointer to the next open space.
1995 serialHead = (serialHead + 1) & SERIAL_BUFFER_MASK;
2002 * @defgroup sys System Library Functions
2004 * Generic system functions similiar to the run-time C library.
2010 * Calculate the CRC-16 CCITT of buffer that is length bytes long.
2011 * The crc parameter allow the calculation on the CRC on multiple buffers.
2013 * @param buffer Pointer to data buffer.
2014 * @param length number of bytes in data buffer
2015 * @param crc starting value
2017 * @return CRC-16 of buffer[0 .. length]
2019 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
2021 uint8_t i, bit, value;
2023 for (i = 0; i < length; ++i)
2027 for (bit = 0; bit < 8; ++bit)
2029 crc ^= (value & 0x01);
2030 crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
2035 return crc ^ 0xffff;
2039 * Initialize the system library and global resources.
2044 output_high (IO_LED);
2046 output_high (IO_CS);
2047 output_low (IO_PS1);
2048 output_low (IO_PS0);
2049 output_low (IO_OSK);
2050 output_low (IO_UPDATE);
2051 output_low (IO_PTT);
2052 output_low (IO_GPS_TXD);
2054 // Configure the port direction (input/output).
2059 // Display a startup message during boot.
2060 fprintf (PC_HOST, "System booted.\n\r");
2064 * Log the current GPS position.
2066 void sysLogGPSData()
2069 logType (LOG_COORD);
2070 logUint8 (gpsPosition.hours);
2071 logUint8 (gpsPosition.minutes);
2072 logUint8 (gpsPosition.seconds);
2073 logInt32 (gpsPosition.latitude);
2074 logInt32 (gpsPosition.longitude);
2075 logInt32 (gpsPosition.altitudeCM);
2077 logUint16 (gpsPosition.vSpeed);
2078 logUint16 (gpsPosition.hSpeed);
2079 logUint16 (gpsPosition.heading);
2081 logUint16 (gpsPosition.status);
2083 logUint8 ((uint8_t) (gpsPosition.dop & 0xff));
2084 logUint8 ((uint8_t) ((gpsPosition.visibleSats << 4) | gpsPosition.trackedSats));
2088 * Log the ADC values of the bus and reference voltage values.
2090 void sysLogVoltage()
2092 logType (LOG_VOLTAGE);
2093 logUint16 (adcRawBusVolt());
2094 logUint16 (adcRawRefVolt());
2100 * @defgroup rtc Real Time Interrupt tick
2102 * Manage the built-in real time interrupt. The interrupt clock PRI is 104uS (9600 bps).
2107 /// A counter that ticks every 100mS.
2110 /// Counts the number of 104uS interrupts for a 100mS time period.
2111 uint16_t timeInterruptCount;
2113 /// Counts the number of 100mS time periods in 1 second.
2116 /// System time in seconds.
2117 uint8_t timeSeconds;
2119 /// System time in minutes.
2120 uint8_t timeMinutes;
2122 /// System time in hours.
2125 /// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
2126 uint8_t timeDutyCycle;
2128 /// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
2129 uint16_t timeCompare;
2131 /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
2134 /// Audio tone NCO update step (phase).
2135 uint16_t timeNCOFreq;
2137 /// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud)
2138 uint8_t timeLowRateCount;
2140 /// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK)
2141 TNC_DATA_MODE tncDataMode;
2143 /// Flag set true once per second.
2144 bool_t timeUpdateFlag;
2146 /// Flag that indicate the flight time should run.
2149 /// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
2150 #define TIME_RATE 125
2153 * Running 8-bit counter that ticks every 100mS.
2155 * @return 100mS time tick
2157 uint8_t timeGetTicks()
2163 * Initialize the real-time clock.
2168 timeInterruptCount = 0;
2173 timeDutyCycle = TIME_DUTYCYCLE_70;
2174 timeCompare = TIME_RATE;
2175 timeUpdateFlag = false;
2177 timeLowRateCount = 0;
2178 timeNCOFreq = 0x2000;
2179 tncDataMode = TNC_MODE_STANDBY;
2180 timeRunFlag = false;
2182 // Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS
2184 set_timer1(timeCompare);
2185 setup_ccp1( CCP_COMPARE_INT );
2186 setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
2190 * Function return true once a second based on real-time clock.
2192 * @return true on one second tick; otherwise false
2194 bool_t timeIsUpdate()
2198 timeUpdateFlag = false;
2206 * Set the blink duty cycle of the heartbeat LED. The LED blinks at a 1Hz rate.
2208 * @param dutyCycle TIME_DUTYCYCLE_xx constant
2210 void timeSetDutyCycle (uint8_t dutyCycle)
2212 timeDutyCycle = dutyCycle;
2216 * Set a flag to indicate the flight time should run. This flag is typically set when the payload
2219 void timeSetRunFlag()
2226 * Timer interrupt handler called every 104uS (9600 times/second).
2230 // Setup the next interrupt for the operational mode.
2231 timeCompare += TIME_RATE;
2232 CCP_1 = timeCompare;
2234 switch (tncDataMode)
2236 case TNC_MODE_STANDBY:
2239 case TNC_MODE_1200_AFSK:
2240 ddsSetFTW (freqTable[timeNCO >> 8]);
2242 timeNCO += timeNCOFreq;
2244 if (++timeLowRateCount == 8)
2246 timeLowRateCount = 0;
2251 case TNC_MODE_9600_FSK:
2256 // Read the GPS serial port and save any incoming characters.
2259 // Count the number of milliseconds required for the tenth second counter.
2260 if (++timeInterruptCount == 960)
2262 timeInterruptCount = 0;
2264 // This timer just ticks every 100mS and is used for general timing.
2267 // Roll the counter over every second.
2268 if (++time100mS == 10)
2272 // We set this flag true every second.
2273 timeUpdateFlag = true;
2275 // Maintain a Real Time Clock.
2277 if (++timeSeconds == 60)
2281 if (++timeMinutes == 60)
2285 } // END if timeMinutes
2286 } // END if timeSeconds
2287 } // END if time100mS
2289 // Flash the status LED at timeDutyCycle % per second. We use the duty cycle for mode feedback.
2290 if (time100mS >= timeDutyCycle)
2291 output_low (IO_LED);
2293 output_high (IO_LED);
2300 * @defgroup tnc TNC (Terminal Node Controller)
2302 * Functions that provide a subset of the TNC functions.
2307 /// The number of start flag bytes to send before the packet message. (360bits * 1200bps = 300mS)
2308 #define TNC_TX_DELAY 45
2310 /// The size of the TNC output buffer.
2311 #define TNC_BUFFER_SIZE 80
2313 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
2314 enum TNC_TX_1200BPS_STATE
2316 /// Stand by state ready to accept new message.
2319 /// 0x7E bit stream pattern used to define start of APRS message.
2322 /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
2325 /// Transmit the message data.
2328 /// Transmit the end flag sequence.
2332 /// Enumeration of the messages we can transmit.
2333 enum TNC_MESSAGE_TYPE
2335 /// Startup message that contains software version information.
2338 /// Plain text status message.
2341 /// Message that contains GPS NMEA-0183 $GPGGA message.
2344 /// Message that contains GPS NMEA-0183 $GPRMC message.
2348 /// AX.25 compliant packet header that contains destination, station call sign, and path.
2349 /// 0x76 for SSID-11, 0x78 for SSID-12
2350 uint8_t TNC_AX25_HEADER[30] = {
2351 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
2352 'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \
2353 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
2354 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
2358 /// The next bit to transmit.
2361 /// Current mode of the 1200 bps state machine.
2362 TNC_TX_1200BPS_STATE tncMode;
2364 /// Counter for each bit (0 - 7) that we are going to transmit.
2365 uint8_t tncBitCount;
2367 /// A shift register that holds the data byte as we bit shift it for transmit.
2370 /// Index into the APRS header and data array for each byte as we transmit it.
2373 /// The number of bytes in the message portion of the AX.25 message.
2376 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
2377 uint8_t tncBitStuff;
2379 /// Pointer to TNC buffer as we save each byte during message preparation.
2380 uint8_t *tncBufferPnt;
2382 /// The type of message to tranmit in the next packet.
2383 TNC_MESSAGE_TYPE tncPacketType;
2385 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
2386 uint8_t tncBuffer[TNC_BUFFER_SIZE];
2388 /// Flag that indicates we want to transmit every 5 seconds.
2389 bool_t tncHighRateFlag;
2392 * Initialize the TNC internal variables.
2397 tncMode = TNC_TX_READY;
2398 tncPacketType = TNC_BOOT_MESSAGE;
2399 tncHighRateFlag = false;
2403 * Determine if the hardware if ready to transmit a 1200 baud packet.
2405 * @return true if ready; otherwise false
2409 if (tncMode == TNC_TX_READY)
2415 void tncHighRate(bool_t state)
2417 tncHighRateFlag = state;
2421 * Configure the TNC for the desired data mode.
2423 * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
2425 void tncSetMode(TNC_DATA_MODE dataMode)
2429 case TNC_MODE_1200_AFSK:
2430 ddsSetMode (DDS_MODE_AFSK);
2433 case TNC_MODE_9600_FSK:
2434 ddsSetMode (DDS_MODE_FSK);
2436 // FSK tones at 445.947 and 445.953 MHz
2437 ddsSetFSKFreq (955382980, 955453621);
2441 tncDataMode = dataMode;
2445 * Determine if the seconds value timeSeconds is a valid time slot to transmit
2446 * a message. Time seconds is in UTC.
2448 * @param timeSeconds UTC time in seconds
2450 * @return true if valid time slot; otherwise false
2452 bool_t tncIsTimeSlot (uint8_t timeSeconds)
2454 if (tncHighRateFlag)
2456 if ((timeSeconds % 5) == 0)
2462 switch (timeSeconds)
2476 * Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
2477 * The provides the pre and postamble as well as the bit stuffed data stream.
2479 void tnc1200TimerTick()
2481 // Set the A-FSK frequency.
2482 if (tncTxBit == 0x00)
2483 timeNCOFreq = 0x2000;
2485 timeNCOFreq = 0x3aab;
2490 // Generate a test signal alteranting between high and low tones.
2491 tncTxBit = (tncTxBit == 0 ? 1 : 0);
2495 // The variable tncShift contains the lastest data byte.
2496 // NRZI enocde the data stream.
2497 if ((tncShift & 0x01) == 0x00)
2503 // When the flag is done, determine if we need to send more or data.
2504 if (++tncBitCount == 8)
2509 // Once we transmit x mS of flags, send the data.
2510 // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
2511 if (++tncIndex == TNC_TX_DELAY)
2514 tncShift = TNC_AX25_HEADER[0];
2516 tncMode = TNC_TX_HEADER;
2519 tncShift = tncShift >> 1;
2523 // Determine if we have sent 5 ones in a row, if we have send a zero.
2524 if (tncBitStuff == 0x1f)
2535 // The variable tncShift contains the lastest data byte.
2536 // NRZI enocde the data stream.
2537 if ((tncShift & 0x01) == 0x00)
2543 // Save the data stream so we can determine if bit stuffing is
2544 // required on the next bit time.
2545 tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
2547 // If all the bits were shifted, get the next byte.
2548 if (++tncBitCount == 8)
2552 // After the header is sent, then send the data.
2553 if (++tncIndex == sizeof(TNC_AX25_HEADER))
2556 tncShift = tncBuffer[0];
2557 tncMode = TNC_TX_DATA;
2559 tncShift = TNC_AX25_HEADER[tncIndex];
2562 tncShift = tncShift >> 1;
2567 // Determine if we have sent 5 ones in a row, if we have send a zero.
2568 if (tncBitStuff == 0x1f)
2579 // The variable tncShift contains the lastest data byte.
2580 // NRZI enocde the data stream.
2581 if ((tncShift & 0x01) == 0x00)
2587 // Save the data stream so we can determine if bit stuffing is
2588 // required on the next bit time.
2589 tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
2591 // If all the bits were shifted, get the next byte.
2592 if (++tncBitCount == 8)
2596 // If everything was sent, transmit closing flags.
2597 if (++tncIndex == tncLength)
2601 tncMode = TNC_TX_END;
2603 tncShift = tncBuffer[tncIndex];
2606 tncShift = tncShift >> 1;
2611 // The variable tncShift contains the lastest data byte.
2612 // NRZI enocde the data stream.
2613 if ((tncShift & 0x01) == 0x00)
2619 // If all the bits were shifted, get the next one.
2620 if (++tncBitCount == 8)
2625 // Transmit two closing flags.
2626 if (++tncIndex == 2)
2628 tncMode = TNC_TX_READY;
2630 // Tell the TNC time interrupt to stop generating the frequency words.
2631 tncDataMode = TNC_MODE_STANDBY;
2634 output_low (IO_OSK);
2635 output_low (IO_PTT);
2636 ddsSetMode (DDS_MODE_POWERDOWN);
2641 tncShift = tncShift >> 1;
2648 * Method that is called every 104uS to transmit the 9600bps FSK data stream.
2650 void tnc9600TimerTick()
2656 * Write character to the TNC buffer. Maintain the pointer
2657 * and length to the buffer. The pointer tncBufferPnt and tncLength
2658 * must be set before calling this function for the first time.
2660 * @param character to save to telemetry buffer
2662 void tncTxByte (uint8_t character)
2664 *tncBufferPnt++ = character;
2669 * Generate the GPS NMEA standard UTC time stamp. Data is written through the tncTxByte
2670 * callback function.
2674 // UTC of position fix.
2675 printf (tncTxByte, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
2679 * Generate the GPS NMEA standard latitude/longitude fix. Data is written through the tncTxByte
2680 * callback function.
2685 uint32_t coord, coordMin;
2688 coord = gpsPosition.latitude;
2690 if (gpsPosition.latitude < 0)
2692 coord = gpsPosition.latitude * -1;
2695 coord = gpsPosition.latitude;
2699 coordMin = (coord % 3600000) / 6;
2700 printf (tncTxByte, "%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
2704 if (gpsPosition.longitude < 0)
2706 coord = gpsPosition.longitude * - 1;
2709 coord = gpsPosition.longitude;
2713 coordMin = (coord % 3600000) / 6;
2714 printf (tncTxByte, "%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
2719 * Generate the GPS NMEA-0183 $GPGGA packet. Data is written through the tncTxByte
2720 * callback function.
2722 void tncGPGGAPacket()
2724 // Generate the GPGGA message.
2725 printf (tncTxByte, "$GPGGA,");
2727 // Standard NMEA time.
2730 // Standard NMEA-0183 latitude/longitude.
2733 // GPS status where 0: not available, 1: available
2734 if (gpsGetFixType() != GPS_NO_FIX)
2735 printf (tncTxByte, "1,");
2737 printf (tncTxByte, "0,");
2739 // Number of visible birds.
2740 printf (tncTxByte, "%02d,", gpsPosition.trackedSats);
2743 printf (tncTxByte, "%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10);
2745 // Altitude in meters.
2746 printf (tncTxByte, "%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100));
2748 // Checksum, we add 1 to skip over the $ character.
2749 printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
2753 * Generate the GPS NMEA-0183 $GPRMC packet. Data is written through the tncTxByte
2754 * callback function.
2756 void tncGPRMCPacket()
2760 // Generate the GPRMC message.
2761 printf (tncTxByte, "$GPRMC,");
2763 // Standard NMEA time.
2767 if (gpsGetFixType() != GPS_NO_FIX)
2768 printf (tncTxByte, "A,");
2770 printf (tncTxByte, "V,");
2772 // Standard NMEA-0183 latitude/longitude.
2775 // Speed knots and heading.
2776 temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826;
2777 printf (tncTxByte, "%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10);
2780 printf (tncTxByte, "%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100);
2782 // Checksum, skip over the $ character.
2783 printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
2787 * Generate the plain text status packet. Data is written through the tncTxByte
2788 * callback function.
2790 void tncStatusPacket(int16_t temperature)
2794 // Plain text telemetry.
2795 printf (tncTxByte, ">ANSR ");
2797 // Display the flight time.
2798 printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds);
2800 // Altitude in feet.
2801 printf (tncTxByte, "%ld' ", gpsPosition.altitudeFeet);
2803 // Peak altitude in feet.
2804 printf (tncTxByte, "%ld'pk ", gpsGetPeakAltitude());
2807 printf (tncTxByte, "%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10);
2809 // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix.
2810 switch (gpsGetFixType())
2813 printf (tncTxByte, "dop ");
2817 printf (tncTxByte, "hdop ");
2822 printf (tncTxByte, "pdop ");
2826 // Number of satellites in the solution.
2827 printf (tncTxByte, "%utrk ", gpsPosition.trackedSats);
2829 // Display main bus voltage.
2830 voltage = adcGetMainBusVolt();
2831 printf (tncTxByte, "%lu.%02luvdc ", voltage / 100, voltage % 100);
2833 // Display internal temperature.
2834 printf (tncTxByte, "%ld.%01ldF ", temperature / 10, abs(temperature % 10));
2836 // Print web address link.
2837 printf (tncTxByte, "www.kd7lmo.net");
2841 * Prepare an AX.25 data packet. Each time this method is called, it automatically
2842 * rotates through 1 of 3 messages.
2844 * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
2846 void tncTxPacket(TNC_DATA_MODE dataMode)
2848 int16_t temperature;
2851 // Only transmit if there is not another message in progress.
2852 if (tncMode != TNC_TX_READY)
2855 // Log the battery and reference voltage before we start the RF chain.
2858 // We need to read the temperature sensor before we setup the DDS since they share a common clock pin.
2859 temperature = lm92GetTemp();
2861 // Log the system temperature every time we transmit a packet.
2862 logType (LOG_TEMPERATURE);
2863 logInt16 (temperature);
2865 // Configure the DDS for the desired operational.
2866 tncSetMode (dataMode);
2868 // Set a pointer to our TNC output buffer.
2869 tncBufferPnt = tncBuffer;
2871 // Set the message length counter.
2874 // Determine the contents of the packet.
2875 switch (tncPacketType)
2877 case TNC_BOOT_MESSAGE:
2878 printf (tncTxByte, ">ANSR Pico Beacon - V3.05");
2880 // Select the next packet we will generate.
2881 tncPacketType = TNC_STATUS;
2885 tncStatusPacket(temperature);
2887 // Select the next packet we will generate.
2888 tncPacketType = TNC_GGA;
2894 // Select the next packet we will generate.
2895 tncPacketType = TNC_RMC;
2901 // Select the next packet we will generate.
2902 tncPacketType = TNC_STATUS;
2906 // Add the end of message character.
2907 printf (tncTxByte, "\015");
2909 // Calculate the CRC for the header and message.
2910 crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
2911 crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
2913 // Save the CRC in the message.
2914 *tncBufferPnt++ = crc & 0xff;
2915 *tncBufferPnt = (crc >> 8) & 0xff;
2917 // Update the length to include the CRC bytes.
2920 // Prepare the variables that are used in the real-time clock interrupt.
2925 tncMode = TNC_TX_SYNC;
2927 // Turn on the PA chain.
2928 output_high (IO_PTT);
2930 // Wait for the PA chain to power up.
2934 output_high (IO_OSK);
2936 // Log the battery and reference voltage just after we key the transmitter.
2945 uint8_t streamIndex;
2948 uint8_t bitStream[] = { 0x10, 0x20, 0x30 };
2955 value = bitStream[0];
2960 counter += 0x10622d;
2962 CCP_1 = (uint16_t) ((counter >> 16) & 0xffff);
2964 if ((value & 0x80) == 0x80)
2965 setup_ccp1 (CCP_COMPARE_SET_ON_MATCH);
2967 setup_ccp1 (CCP_COMPARE_CLR_ON_MATCH);
2969 if (++bitIndex == 8)
2973 if (++streamIndex == sizeof(bitStream))
2978 value = bitStream[streamIndex];
2983 // This is where we go after reset.
2986 uint8_t i, utcSeconds, lockLostCounter;
2990 // Configure the basic systems.
2993 // Wait for the power converter chains to stabilize.
2996 // Setup the subsystems.
3008 // Turn off the LED after everything is configured.
3009 output_low (IO_LED);
3011 // Check for the diagnostics plug, otherwise we'll continue to boot.
3014 // Setup our interrupts.
3015 enable_interrupts(GLOBAL);
3016 enable_interrupts(INT_CCP1);
3018 // Turn on the GPS engine.
3021 // Allow the GPS engine to boot.
3024 // Initialize the GPS engine.
3025 while (!gpsSetup());
3027 // Charge the ADC filters.
3028 for (i = 0; i < 32; ++i)
3031 // Log startup event.
3032 logType (LOG_BOOTED);
3033 logUint8 (gpsPosition.month);
3034 logUint8 (gpsPosition.day);
3035 logUint8 (gpsPosition.year & 0xff);
3037 logUint8 (gpsPosition.hours);
3038 logUint8 (gpsPosition.minutes);
3039 logUint8 (gpsPosition.seconds);
3041 // Transmit software version packet on start up.
3042 tncTxPacket(TNC_MODE_1200_AFSK);
3044 // Counters to send packets if the GPS time stamp is not available.
3045 lockLostCounter = 5;
3048 // This is the main loop that process GPS data and waits for the once per second timer tick.
3051 // Read the GPS engine serial port FIFO and process the GPS data.
3056 // Start the flight timer when we get a valid 3D fix.
3057 if (gpsGetFixType() == GPS_3D_FIX)
3060 // Generate our packets based on the GPS time.
3061 if (tncIsTimeSlot(gpsPosition.seconds))
3062 tncTxPacket(TNC_MODE_1200_AFSK);
3064 // Sync the internal clock to GPS UTC time.
3065 utcSeconds = gpsPosition.seconds;
3067 // This counter is reset every time we receive the GPS message.
3068 lockLostCounter = 0;
3070 // Log the data to flash.
3072 } // END if gpsIsReady
3074 // Processing that occurs once a second.
3077 // We maintain the UTC time in seconds if we shut off the GPS engine or it fails.
3078 if (++utcSeconds == 60)
3081 // If we loose information for more than 5 seconds,
3082 // we will determine when to send a packet based on internal time.
3083 if (lockLostCounter == 5)
3085 if (tncIsTimeSlot(utcSeconds))
3086 tncTxPacket(TNC_MODE_1200_AFSK);
3090 // Update the ADC filters.
3093 if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0)
3096 } // END if timeIsUpdate