altos: Add test scaffolding for APRS
[fw/altos] / src / drivers / ao_aprs.c
1 /** 
2  * http://ad7zj.net/kd7lmo/aprsbeacon_code.html
3  *
4  * @mainpage Pico Beacon
5  *
6  * @section overview_sec Overview
7  *
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.
11
12
13  *
14  * @section history_sec Revision History
15  *
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.
18  *
19  *
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.
25  *
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.
34  *
35  *
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.
40  *
41  * 
42  * @subsection v301 V3.01
43  * 13 Jan 2005, Renamed project and files to Pico Beacon.
44  *
45  *
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.
56  *
57  * 
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.
61  *
62  * 
63  * @subsection v200 V2.00
64  * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor,
65  *                                     (2) serial EEPROM, 
66  *                                     (3) GPS power control, 
67  *                                     (4) additional ADC input, and 
68  *                                     (5) LM60 temperature sensor.                            
69  *
70  *
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.
74  *
75  *
76  * @subsection v100 V1.00
77  * 25 Sep 2001, Initial release.  Flew ANSR-3 and ANSR-4.
78  * 
79
80
81  *
82  *
83  * @section copyright_sec Copyright
84  *
85  * Copyright (c) 2001-2009 Michael Gray, KD7LMO
86
87
88  *
89  *
90  * @section gpl_sec GNU General Public License
91  *
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.
96  *
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.
101  *
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
105  *  
106
107
108  * 
109  * 
110  * @section design Design Details
111  *
112  * Provides design details on a variety of the components that make up the Pico Beacon.
113  *
114  *  @subpage power
115  */
116
117 /**
118  *  @page power Power Consumption
119  *
120  *  Measured DC power consumption.
121  * 
122  *  3VDC prime power current 
123
124  *
125  *    7mA Held in reset 
126
127  *   18mA Processor running, all I/O off 
128
129  *  110mA GPS running 
130
131  *  120mA GPS running w/antenna 
132
133  *  250mA DDS running and GPS w/antenna 
134
135  *  420mA DDS running, GPS w/antenna, and PA chain on with no RF 
136
137  *  900mA Transmit 
138
139  *
140  */
141
142 #ifndef AO_APRS_TEST
143 #include <ao.h>
144 #endif
145
146 #include <ao_aprs.h>
147
148 typedef int bool_t;
149 typedef int32_t int32;
150 #define false 0
151 #define true 1
152
153 // Public methods, constants, and data structures for each class.
154
155 /// Operational modes of the AD9954 DDS for the ddsSetMode function.
156 typedef enum
157 {
158     /// Device has not been initialized.
159     DDS_MODE_NOT_INITIALIZED,
160
161     /// Device in lowest power down mode.
162     DDS_MODE_POWERDOWN,
163
164     /// Generate FM modulated audio tones.
165     DDS_MODE_AFSK,
166
167     /// Generate true FSK tones.
168     DDS_MODE_FSK
169 }  DDS_MODE;
170
171 void ddsInit();
172 void ddsSetAmplitude (uint8_t amplitude);
173 void ddsSetOutputScale (uint16_t amplitude);
174 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
175 void ddsSetFreq (uint32_t freq);
176 void ddsSetFTW (uint32_t ftw);
177 void ddsSetMode (DDS_MODE mode);
178
179 /// Type of GPS fix.
180 typedef enum
181 {
182     /// No GPS FIX
183     GPS_NO_FIX,
184
185     /// 2D (Latitude/Longitude) fix.
186     GPS_2D_FIX,
187
188     /// 3D (Latitude/Longitude/Altitude) fix.
189     GPS_3D_FIX
190 }  GPS_FIX_TYPE;
191
192 /// GPS Position information.
193 typedef struct 
194 {
195     /// Flag that indicates the position information has been updated since it was last checked.
196     bool_t updateFlag;
197
198     /// Month in UTC time.
199     uint8_t month;
200
201     /// Day of month in UTC time.
202     uint8_t day;
203
204     /// Hours in UTC time.
205     uint8_t hours;
206
207     /// Minutes in UTC time.
208     uint8_t minutes;
209
210     /// Seconds in UTC time.
211     uint8_t seconds;
212
213     /// Year in UTC time.
214     uint16_t year;
215
216     /// Latitude in milli arc-seconds where + is North, - is South.
217     int32_t latitude;
218
219     /// Longitude in milli arc-seconds where + is East, - is West.
220     int32_t longitude;
221
222     /// Altitude in cm
223     int32_t altitudeCM;
224
225     /// Calculated altitude in feet
226     int32_t altitudeFeet;
227
228     /// 3D speed in cm/second.
229     uint16_t vSpeed;
230
231     /// 2D speed in cm/second.
232     uint16_t hSpeed;
233
234     /// Heading units of 0.1 degrees.
235     uint16_t heading;
236
237     /// DOP (Dilution of Precision)
238     uint16_t dop;
239
240     /// 16-bit number that represents status of GPS engine.
241     uint16_t status;
242
243     /// Number of tracked satellites used in the fix position.
244     uint8_t trackedSats;
245
246     /// Number of visible satellites.
247     uint8_t visibleSats;
248 } GPSPOSITION_STRUCT;
249
250 GPSPOSITION_STRUCT gpsPosition;
251
252 void gpsInit();
253 bool_t gpsIsReady();
254 GPS_FIX_TYPE gpsGetFixType();
255 int32_t gpsGetPeakAltitude();
256 void gpsPowerOn();
257 bool_t gpsSetup();
258 void gpsUpdate();
259
260 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
261
262 uint8_t timeGetTicks();
263 void timeInit();
264 void timeSetDutyCycle (uint8_t dutyCycle);
265 void timeUpdate();
266
267 /// Operational modes of the TNC for the tncSetMode function.
268 typedef enum
269 {
270     /// No operation waiting for setup and configuration.
271     TNC_MODE_STANDBY, 
272
273     /// 1200 bps using A-FSK (Audio FSK) tones.
274     TNC_MODE_1200_AFSK,
275
276     /// 9600 bps using true FSK tones.
277     TNC_MODE_9600_FSK
278 } TNC_DATA_MODE;
279
280 void tncInit();
281 bool_t tncIsFree();
282 void tncHighRate(bool_t state);
283 void tncSetMode (TNC_DATA_MODE dataMode);
284 void tnc1200TimerTick();
285 void tnc9600TimerTick();
286 void tncTxByte (uint8_t value);
287 void tncTxPacket(TNC_DATA_MODE dataMode);
288
289 /** @} */
290
291 /**
292  *  @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
293  *
294  *  Functions to control the Analog Devices AD9954 DDS.
295  *
296  *  @{
297  */
298
299 /// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings.
300 #define DDS_AD9954_CFR1 0x00
301
302 /// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current.
303 #define DDS_AD9954_CFR2 0x01
304
305 /// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff).
306 #define DDS_AD9954_ASF 0x02
307
308 /// AD9954 ARR - Amplitude ramp rate for OSK function.
309 #define DDS_AD9954_ARR 0x03
310
311 /// AD9954 FTW0 - Frequency tuning word 0.
312 #define DDS_AD9954_FTW0 0x04
313
314 /// AD9954 FTW1 - Frequency tuning word 1
315 #define DDS_AD9954_FTW1 0x06
316
317 /// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode
318 #define DDS_AD9954_NLSCW 0x07
319
320 /// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode
321 #define DDS_AD9954_PLSCW 0x08
322
323 /// AD9954 RSCW0 - RAM Segment Control Word 0
324 #define DDS_AD9954_RWCW0 0x07
325
326 /// AD9954 RSCW0 - RAM Segment Control Word 1
327 #define DDS_AD9954_RWCW1 0x08
328
329 /// AD9954 RAM segment
330 #define DDS_RAM 0x0b
331
332 /// Current operational mode.
333 DDS_MODE ddsMode;
334
335 /// Number of digits in DDS frequency to FTW conversion.
336 #define DDS_FREQ_TO_FTW_DIGITS 9
337
338 /// Array of multiplication factors used to convert frequency to the FTW.
339 const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
340
341 /// Array of divisors used to convert frequency to the FTW.
342 const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
343
344 /// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
345 const uint16_t DDS_AMP_TO_SCALE[] = 
346
347     16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, 
348     8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, 
349     3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 
350 };
351
352
353 /// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz  (445.950MHz)
354 const uint32_t freqTable[256] = 
355 {
356     955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, 
357     955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, 
358     955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, 
359     955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, 
360     955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, 
361     955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, 
362     955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, 
363     955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, 
364     955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, 
365     955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, 
366     955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, 
367     955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, 
368     955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, 
369     955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, 
370     955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, 
371     955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, 
372     955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, 
373     955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, 
374     955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, 
375     955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, 
376     955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, 
377     955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, 
378     955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, 
379     955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, 
380     955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, 
381     955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, 
382     955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, 
383     955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, 
384     955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, 
385     955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, 
386     955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, 
387     955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
388 };
389
390 /**
391  *  Set DDS frequency tuning word.  The output frequency is equal to RefClock * (ftw / 2 ^ 32).
392  *
393  *  @param ftw Frequency Tuning Word
394  */
395 void ddsSetFTW (uint32_t ftw)
396 {
397     int x = ftw - freqTable[0];
398     putchar (x > 0 ? 0xff : 0x0);
399 }
400
401 /**
402  *   Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word).
403  *
404  *   @param freq frequency in Hertz
405  *
406  */
407 void ddsSetFreq(uint32_t freq)
408 {
409     uint8_t i;
410     uint32_t ftw;
411     
412     // To avoid rounding errors with floating point math, we do a long multiply on the data.
413     ftw = freq * DDS_MULT[0];
414     
415     for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i)
416         ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i];
417     
418     ddsSetFTW (ftw);
419 }
420
421 /**
422  *  Set DDS frequency tuning word for the FSK 0 and 1 values.  The output frequency is equal 
423  *  to RefClock * (ftw / 2 ^ 32).
424  *
425  *  @param ftw0 frequency tuning word for the FSK 0 value
426  *  @param ftw1 frequency tuning word for the FSK 1 value
427  */
428 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1)
429 {
430 //      printf ("ftw0 %d ftw1 %d\n", ftw0, ftw1);
431 }
432
433 /** 
434  *   Set the DDS to run in A-FSK, FSK, or PSK31 mode
435  *
436  *   @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant
437  */
438 void ddsSetMode (DDS_MODE mode)
439 {
440 //      printf ("mode %d\n", mode);
441 }
442
443 /** @} */
444
445 /**
446  *  @defgroup GPS Motorola M12+ GPS Engine
447  *
448  *  Functions to control the Motorola M12+ GPS engine in native binary protocol mode.
449  *
450  *  @{
451  */
452
453 /// The maximum length of a binary GPS engine message.
454 #define GPS_BUFFER_SIZE 50
455
456 /// GPS parse engine state machine values.
457 typedef enum
458 {
459     /// 1st start character '@'
460     GPS_START1,
461
462     /// 2nd start character '@'
463     GPS_START2,
464
465     /// Upper case 'A' - 'Z' message type
466     GPS_COMMAND1,
467
468     /// Lower case 'a' - 'z' message type
469     GPS_COMMAND2,
470
471     /// 0 - xx bytes based on message type 'Aa'
472     GPS_READMESSAGE,
473
474     /// 8-bit checksum
475     GPS_CHECKSUMMESSAGE,
476
477     /// End of message - Carriage Return
478     GPS_EOMCR,
479
480     /// End of message - Line Feed
481     GPS_EOMLF
482 } GPS_PARSE_STATE_MACHINE;
483
484 /// Index into gpsBuffer used to store message data.
485 uint8_t gpsIndex;
486
487 /// State machine used to parse the GPS message stream.
488 GPS_PARSE_STATE_MACHINE gpsParseState;
489
490 /// Buffer to store data as it is read from the GPS engine.
491 uint8_t gpsBuffer[GPS_BUFFER_SIZE]; 
492
493 /// Peak altitude detected while GPS is in 3D fix mode.
494 int32_t gpsPeakAltitude;
495
496 /// Checksum used to verify binary message from GPS engine.
497 uint8_t gpsChecksum;
498
499 /// Last verified GPS message received.
500 GPSPOSITION_STRUCT gpsPosition;
501
502 /**
503  *   Get the type of fix.
504  *
505  *   @return gps fix type enumeration
506  */
507 GPS_FIX_TYPE gpsGetFixType()
508 {
509     // The upper 3-bits determine the fix type.
510     switch (gpsPosition.status & 0xe000) 
511     {
512         case 0xe000:
513             return GPS_3D_FIX;
514
515         case 0xc000:
516             return GPS_2D_FIX;
517     
518         default:
519             return GPS_NO_FIX;
520     } // END switch
521 }
522
523 /**
524  *  Peak altitude detected while GPS is in 3D fix mode since the system was booted.
525  *
526  *  @return altitude in feet
527  */
528 int32_t gpsGetPeakAltitude()
529 {
530     return gpsPeakAltitude;
531 }
532
533 /** 
534  *    Initialize the GPS subsystem.
535  */
536 void gpsInit()
537 {
538     // Initial parse state.
539     gpsParseState = GPS_START1;
540
541     // Assume we start at sea level.
542     gpsPeakAltitude = 0;
543
544     // Clear the structure that stores the position message.
545     memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT));
546
547     // Setup the timers used to measure the 1-PPS time period.
548 //    setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
549 //    setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3);
550 }
551
552 /**
553  *   Determine if new GPS message is ready to process.  This function is a one shot and
554  *   typically returns true once a second for each GPS position fix.
555  *
556  *   @return true if new message available; otherwise false
557  */
558 bool_t gpsIsReady()
559 {
560     return true;
561     if (gpsPosition.updateFlag) 
562     {
563         gpsPosition.updateFlag = false;
564         return true;
565     } // END if
566
567     return false;
568 }
569
570 /**
571  *   Calculate NMEA-0183 message checksum of buffer that is length bytes long.
572  *
573  *   @param buffer pointer to data buffer.
574  *   @param length number of bytes in buffer.
575  *
576  *   @return checksum of buffer
577  */
578 uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length)
579 {
580     uint8_t i, checksum;
581
582     checksum = 0;
583
584     for (i = 0; i < length; ++i)
585         checksum ^= buffer[i];
586
587     return checksum;
588 }
589
590 /**
591  *   Verify the GPS engine is sending the @@Hb position report message.  If not,
592  *   configure the GPS engine to send the desired report.
593  *
594  *   @return true if GPS engine operation; otherwise false
595  */
596 bool_t gpsSetup()
597 {
598     uint8_t startTime, retryCount;
599
600     // We wait 10 seconds for the GPS engine to respond to our message request.
601     startTime = timeGetTicks();
602     retryCount = 0;
603
604     while (++retryCount < 10) 
605     {
606         // Read the serial FIFO and process the GPS messages.
607 //        gpsUpdate();
608
609         // If a GPS data set is available, then GPS is operational.
610         if (gpsIsReady()) 
611         {
612 //            timeSetDutyCycle (TIME_DUTYCYCLE_10);
613             return true;
614         }
615
616         if (timeGetTicks() > startTime) 
617         {
618             puts ("@@Hb\001\053\015\012");
619             startTime += 10;
620         } // END if
621             
622     } // END while
623
624     return false;
625 }
626
627 /**
628  *   Parse the Motorola @@Hb (Short position/message) report.
629  */
630 void gpsParsePositionMessage()
631 {
632     // Convert the binary stream into data elements.  We will scale to the desired units
633     // as the values are used.
634     gpsPosition.updateFlag = true;
635
636     gpsPosition.month = gpsBuffer[0];
637     gpsPosition.day = gpsBuffer[1];
638     gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3];
639     gpsPosition.hours = gpsBuffer[4];
640     gpsPosition.minutes = gpsBuffer[5];
641     gpsPosition.seconds = gpsBuffer[6];
642     gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14];
643     gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18];
644     gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22];
645     gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l;
646     gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28];
647     gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30];
648     gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32];
649     gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34];
650     gpsPosition.visibleSats = gpsBuffer[35];
651     gpsPosition.trackedSats = gpsBuffer[36];
652     gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38];
653
654     // Update the peak altitude if we have a valid 3D fix.
655     if (gpsGetFixType() == GPS_3D_FIX)
656         if (gpsPosition.altitudeFeet > gpsPeakAltitude)
657             gpsPeakAltitude = gpsPosition.altitudeFeet;
658 }
659
660 /**
661  *  Turn on the GPS engine power and serial interface.
662  */
663 void gpsPowerOn()
664 {
665     // 3.0 VDC LDO control line.
666 //    output_high (IO_GPS_PWR);
667
668 }
669
670 /**
671  *   Turn off the GPS engine power and serial interface.
672  */
673 void gpsPowerOff()
674 {
675     // 3.0 VDC LDO control line.
676 //    output_low (IO_GPS_PWR);
677 }
678
679 /** @} */
680
681
682 /**
683  *  @defgroup sys System Library Functions
684  *
685  *  Generic system functions similiar to the run-time C library.
686  *
687  *  @{
688  */
689
690 /**
691  *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
692  *    The crc parameter allow the calculation on the CRC on multiple buffers.
693  *
694  *    @param buffer Pointer to data buffer.
695  *    @param length number of bytes in data buffer
696  *    @param crc starting value
697  *
698  *    @return CRC-16 of buffer[0 .. length]
699  */
700 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
701 {
702     uint8_t i, bit, value;
703
704     for (i = 0; i < length; ++i) 
705     {
706         value = buffer[i];
707
708         for (bit = 0; bit < 8; ++bit) 
709         {
710             crc ^= (value & 0x01);
711             crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
712             value = value >> 1;
713         } // END for
714     } // END for
715
716     return crc ^ 0xffff;
717 }
718
719 /** @} */
720
721 /**
722  *  @defgroup rtc Real Time Interrupt tick
723  *
724  *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
725  *
726  *  @{
727  */
728
729 /// A counter that ticks every 100mS.
730 uint8_t timeTicks;
731
732 /// Counts the number of 104uS interrupts for a 100mS time period.
733 uint16_t timeInterruptCount;
734
735 /// Counts the number of 100mS time periods in 1 second.
736 uint8_t time100ms;
737
738 /// System time in seconds.
739 uint8_t timeSeconds;
740
741 /// System time in minutes.
742 uint8_t timeMinutes;
743
744 /// System time in hours.
745 uint8_t timeHours;
746
747 /// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
748 uint8_t timeDutyCycle;
749
750 /// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
751 uint16_t timeCompare;
752
753 /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
754 uint16_t timeNCO;
755
756 /// Audio tone NCO update step (phase).
757 uint16_t timeNCOFreq;
758
759 /// Counter used to deciminate down from the 104uS to 833uS interrupt rate.  (9600 to 1200 baud) 
760 uint8_t timeLowRateCount;
761
762 /// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK)
763 TNC_DATA_MODE tncDataMode;
764
765 /// Flag set true once per second.
766 bool_t timeUpdateFlag;
767
768 /// Flag that indicate the flight time should run.
769 bool_t timeRunFlag;
770
771 /// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
772 #define TIME_RATE 125
773
774 /**
775  *   Running 8-bit counter that ticks every 100mS.
776  *
777  *   @return 100mS time tick
778  */
779 uint8_t timeGetTicks()
780 {
781     return timeTicks;
782 }
783
784 /**
785  *   Initialize the real-time clock.
786  */
787 void timeInit()
788 {
789     timeTicks = 0;
790     timeInterruptCount = 0;
791 //    time100mS = 0;
792     timeSeconds = 0;
793     timeMinutes = 0;
794     timeHours = 0;
795     timeCompare = TIME_RATE;
796     timeUpdateFlag = false;
797     timeNCO = 0x00;
798     timeLowRateCount = 0;
799     timeNCOFreq = 0x2000;
800     tncDataMode = TNC_MODE_STANDBY;  
801     timeRunFlag = false;
802 }
803
804 /**
805  *   Function return true once a second based on real-time clock.
806  *
807  *   @return true on one second tick; otherwise false
808  */
809 bool_t timeIsUpdate()
810 {
811     if (timeUpdateFlag) 
812     {
813         timeUpdateFlag = false;
814         return true;
815     } // END if
816
817     return false;
818 }
819
820 /**
821  *   Set a flag to indicate the flight time should run.  This flag is typically set when the payload
822  *   lifts off.
823  */
824 void timeSetRunFlag()
825 {
826     timeRunFlag = true;
827 }
828
829 /**
830  *   Timer interrupt handler called every 104uS (9600 times/second).
831  */
832 void timeUpdate()
833 {
834     // Setup the next interrupt for the operational mode.
835     timeCompare += TIME_RATE;
836 //    CCP_1 = timeCompare;
837
838     switch (tncDataMode) 
839     {
840         case TNC_MODE_STANDBY:
841             break;
842
843         case TNC_MODE_1200_AFSK:
844             ddsSetFTW (freqTable[timeNCO >> 8]);
845
846             timeNCO += timeNCOFreq;
847
848             if (++timeLowRateCount == 8) 
849             {
850                 timeLowRateCount = 0;
851                 tnc1200TimerTick();
852             } // END if
853             break;
854
855         case TNC_MODE_9600_FSK:
856             tnc9600TimerTick();
857             break;
858     } // END switch
859 }
860
861 /** @} */
862
863 /**
864  *  @defgroup tnc TNC (Terminal Node Controller)
865  *
866  *  Functions that provide a subset of the TNC functions.
867  *
868  *  @{
869  */
870
871 /// The number of start flag bytes to send before the packet message.  (360bits * 1200bps = 300mS)
872 #define TNC_TX_DELAY 45
873
874 /// The size of the TNC output buffer.
875 #define TNC_BUFFER_SIZE 80
876
877 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
878 typedef enum
879 {
880     /// Stand by state ready to accept new message.
881     TNC_TX_READY,
882
883     /// 0x7E bit stream pattern used to define start of APRS message.
884     TNC_TX_SYNC,
885
886     /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
887     TNC_TX_HEADER,
888
889     /// Transmit the message data.
890     TNC_TX_DATA,
891
892     /// Transmit the end flag sequence.
893     TNC_TX_END
894 } TNC_TX_1200BPS_STATE;
895
896 /// Enumeration of the messages we can transmit. 
897 typedef enum
898 {
899     /// Startup message that contains software version information.
900     TNC_BOOT_MESSAGE,
901
902     /// Plain text status message.
903     TNC_STATUS,
904
905     /// Message that contains GPS NMEA-0183 $GPGGA message.
906     TNC_GGA,
907
908     /// Message that contains GPS NMEA-0183 $GPRMC message.
909     TNC_RMC
910 }  TNC_MESSAGE_TYPE;
911
912 /// AX.25 compliant packet header that contains destination, station call sign, and path.
913 /// 0x76 for SSID-11, 0x78 for SSID-12
914 uint8_t TNC_AX25_HEADER[30] = { 
915     'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
916     'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \
917     'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
918     'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
919     0x03, 0xf0 };
920
921
922 /// The next bit to transmit.
923 uint8_t tncTxBit;
924
925 /// Current mode of the 1200 bps state machine.
926 TNC_TX_1200BPS_STATE tncMode;
927
928 /// Counter for each bit (0 - 7) that we are going to transmit.
929 uint8_t tncBitCount;
930
931 /// A shift register that holds the data byte as we bit shift it for transmit.
932 uint8_t tncShift;
933
934 /// Index into the APRS header and data array for each byte as we transmit it.
935 uint8_t tncIndex;
936
937 /// The number of bytes in the message portion of the AX.25 message.
938 uint8_t tncLength;
939
940 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
941 uint8_t tncBitStuff;
942
943 /// Pointer to TNC buffer as we save each byte during message preparation.
944 uint8_t *tncBufferPnt;
945
946 /// The type of message to tranmit in the next packet.
947 TNC_MESSAGE_TYPE tncPacketType;
948
949 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
950 uint8_t tncBuffer[TNC_BUFFER_SIZE];
951
952 /// Flag that indicates we want to transmit every 5 seconds.
953 bool_t tncHighRateFlag;
954
955 /** 
956  *   Initialize the TNC internal variables.
957  */
958 void tncInit()
959 {
960     tncTxBit = 0;
961     tncMode = TNC_TX_READY;
962     tncPacketType = TNC_BOOT_MESSAGE;
963     tncHighRateFlag = false;
964 }
965
966 /**
967  *  Determine if the hardware if ready to transmit a 1200 baud packet.
968  *
969  *  @return true if ready; otherwise false
970  */
971 bool_t tncIsFree()
972 {
973     if (tncMode == TNC_TX_READY)
974         return true;
975
976     return false;
977 }
978
979 void tncHighRate(bool_t state)
980 {
981     tncHighRateFlag = state;
982 }
983
984 /**
985  *   Configure the TNC for the desired data mode.
986  *
987  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
988  */ 
989 void tncSetMode(TNC_DATA_MODE dataMode)
990 {
991     switch (dataMode) 
992     {
993         case TNC_MODE_1200_AFSK:
994             ddsSetMode (DDS_MODE_AFSK);
995             break;
996
997         case TNC_MODE_9600_FSK:
998             ddsSetMode (DDS_MODE_FSK);
999
1000             // FSK tones at 445.947 and 445.953 MHz
1001             ddsSetFSKFreq (955382980, 955453621);
1002             break;
1003         case TNC_MODE_STANDBY:
1004             break;
1005     } // END switch
1006
1007     tncDataMode = dataMode; 
1008 }
1009
1010 /**
1011  *  Determine if the seconds value timeSeconds is a valid time slot to transmit
1012  *  a message.  Time seconds is in UTC.
1013  *
1014  *  @param timeSeconds UTC time in seconds
1015  *
1016  *  @return true if valid time slot; otherwise false
1017  */
1018 bool_t tncIsTimeSlot (uint8_t timeSeconds)
1019 {
1020     if (tncHighRateFlag)
1021     {
1022         if ((timeSeconds % 5) == 0)
1023             return true;
1024
1025         return false;
1026     } // END if
1027
1028     switch (timeSeconds) 
1029     {
1030         case 0:
1031         case 15:
1032         case 30:
1033         case 45:
1034             return true;
1035
1036         default:
1037             return false;
1038     } // END switch
1039 }
1040
1041 /**
1042  *   Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
1043  *   The provides the pre and postamble as well as the bit stuffed data stream.
1044  */
1045 void tnc1200TimerTick()
1046 {
1047     // Set the A-FSK frequency.
1048     if (tncTxBit == 0x00)
1049         timeNCOFreq = 0x2000;
1050     else
1051         timeNCOFreq = 0x3aab;
1052
1053     switch (tncMode) 
1054     {
1055         case TNC_TX_READY:
1056             // Generate a test signal alteranting between high and low tones.
1057             tncTxBit = (tncTxBit == 0 ? 1 : 0);
1058             break;
1059
1060         case TNC_TX_SYNC:
1061             // The variable tncShift contains the lastest data byte.
1062             // NRZI enocde the data stream.
1063             if ((tncShift & 0x01) == 0x00) {
1064                 if (tncTxBit == 0)
1065                     tncTxBit = 1;
1066                 else
1067                     tncTxBit = 0;
1068             }
1069                     
1070             // When the flag is done, determine if we need to send more or data.
1071             if (++tncBitCount == 8) 
1072             {
1073                 tncBitCount = 0;
1074                 tncShift = 0x7e;
1075
1076                 // Once we transmit x mS of flags, send the data.
1077                 // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
1078                 if (++tncIndex == TNC_TX_DELAY) 
1079                 {
1080                     tncIndex = 0;
1081                     tncShift = TNC_AX25_HEADER[0];
1082                     tncBitStuff = 0;
1083                     tncMode = TNC_TX_HEADER;
1084                 } // END if
1085             } else
1086                 tncShift = tncShift >> 1;
1087             break;
1088
1089         case TNC_TX_HEADER:
1090             // Determine if we have sent 5 ones in a row, if we have send a zero.
1091             if (tncBitStuff == 0x1f) 
1092             {
1093                 if (tncTxBit == 0)
1094                     tncTxBit = 1;
1095                 else
1096                     tncTxBit = 0;
1097
1098                 tncBitStuff = 0x00;
1099                 return;
1100             }    // END if
1101
1102             // The variable tncShift contains the lastest data byte.
1103             // NRZI enocde the data stream.
1104             if ((tncShift & 0x01) == 0x00) {
1105                 if (tncTxBit == 0)
1106                     tncTxBit = 1;
1107                 else
1108                     tncTxBit = 0;
1109             }
1110
1111             // Save the data stream so we can determine if bit stuffing is 
1112             // required on the next bit time.
1113             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
1114
1115             // If all the bits were shifted, get the next byte.
1116             if (++tncBitCount == 8) 
1117             {
1118                 tncBitCount = 0;
1119
1120                 // After the header is sent, then send the data.
1121                 if (++tncIndex == sizeof(TNC_AX25_HEADER)) 
1122                 {
1123                     tncIndex = 0;
1124                     tncShift = tncBuffer[0];
1125                     tncMode = TNC_TX_DATA;
1126                 } else
1127                     tncShift = TNC_AX25_HEADER[tncIndex];
1128
1129             } else
1130                 tncShift = tncShift >> 1;
1131
1132             break;
1133
1134         case TNC_TX_DATA:
1135             // Determine if we have sent 5 ones in a row, if we have send a zero.
1136             if (tncBitStuff == 0x1f) 
1137             {
1138                 if (tncTxBit == 0)
1139                     tncTxBit = 1;
1140                 else
1141                     tncTxBit = 0;
1142
1143                 tncBitStuff = 0x00;
1144                 return;
1145             }    // END if
1146
1147             // The variable tncShift contains the lastest data byte.
1148             // NRZI enocde the data stream.
1149             if ((tncShift & 0x01) == 0x00) {
1150                 if (tncTxBit == 0)
1151                     tncTxBit = 1;
1152                 else
1153                     tncTxBit = 0;
1154             }
1155
1156             // Save the data stream so we can determine if bit stuffing is 
1157             // required on the next bit time.
1158             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
1159
1160             // If all the bits were shifted, get the next byte.
1161             if (++tncBitCount == 8) 
1162             {
1163                 tncBitCount = 0;
1164
1165                 // If everything was sent, transmit closing flags.
1166                 if (++tncIndex == tncLength) 
1167                 {
1168                     tncIndex = 0;
1169                     tncShift = 0x7e;
1170                     tncMode = TNC_TX_END;
1171                 } else
1172                     tncShift = tncBuffer[tncIndex];
1173
1174             } else
1175                 tncShift = tncShift >> 1;
1176
1177             break;
1178
1179         case TNC_TX_END:
1180             // The variable tncShift contains the lastest data byte.
1181             // NRZI enocde the data stream. 
1182             if ((tncShift & 0x01) == 0x00) {
1183                 if (tncTxBit == 0)
1184                     tncTxBit = 1;
1185                 else
1186                     tncTxBit = 0;
1187             }
1188
1189             // If all the bits were shifted, get the next one.
1190             if (++tncBitCount == 8) 
1191             {
1192                 tncBitCount = 0;
1193                 tncShift = 0x7e;
1194     
1195                 // Transmit two closing flags.
1196                 if (++tncIndex == 2) 
1197                 {
1198                     tncMode = TNC_TX_READY;
1199
1200                     // Tell the TNC time interrupt to stop generating the frequency words.
1201                     tncDataMode = TNC_MODE_STANDBY;
1202
1203                     // Key off the DDS.
1204 //                    output_low (IO_OSK);
1205 //                    output_low (IO_PTT);
1206                     ddsSetMode (DDS_MODE_POWERDOWN);
1207
1208                     return;
1209                 } // END if
1210             } else
1211                 tncShift = tncShift >> 1;
1212
1213             break;
1214     } // END switch
1215 }
1216
1217 /**
1218  *   Method that is called every 104uS to transmit the 9600bps FSK data stream.
1219  */
1220 void tnc9600TimerTick()
1221 {
1222
1223 }
1224
1225 /**
1226  *    Write character to the TNC buffer.  Maintain the pointer
1227  *    and length to the buffer.  The pointer tncBufferPnt and tncLength
1228  *    must be set before calling this function for the first time.
1229  * 
1230  *    @param character to save to telemetry buffer
1231  */
1232 void tncTxByte (uint8_t character)
1233 {
1234     *tncBufferPnt++ = character;
1235     ++tncLength;
1236 }
1237
1238 static void
1239 tncPrintf(char *fmt, ...)
1240 {
1241     va_list     ap;
1242     int         c;
1243
1244     va_start(ap, fmt);
1245     c = vsprintf((char *) tncBufferPnt, fmt, ap);
1246     va_end(ap);
1247     tncBufferPnt += c;
1248     tncLength += c;
1249 }
1250
1251 /**
1252  *   Generate the GPS NMEA standard UTC time stamp.  Data is written through the tncTxByte
1253  *   callback function.
1254  */
1255 void tncNMEATime()
1256 {
1257     // UTC of position fix.
1258     tncPrintf ("%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
1259 }
1260
1261 /**
1262  *   Generate the GPS NMEA standard latitude/longitude fix.  Data is written through the tncTxByte
1263  *   callback function.
1264  */
1265 void tncNMEAFix()
1266 {
1267     uint8_t dirChar;
1268     uint32_t coord, coordMin;
1269
1270     // Latitude value.
1271     coord = gpsPosition.latitude;
1272
1273     if (gpsPosition.latitude < 0) 
1274     {
1275         coord = gpsPosition.latitude * -1;
1276         dirChar = 'S';
1277     } else {
1278         coord = gpsPosition.latitude;
1279         dirChar = 'N';
1280     }
1281
1282     coordMin = (coord % 3600000) / 6;
1283     tncPrintf ("%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
1284
1285
1286     // Longitude value.
1287     if (gpsPosition.longitude < 0) 
1288     {
1289         coord = gpsPosition.longitude * - 1;
1290         dirChar = 'W';
1291     } else {
1292         coord = gpsPosition.longitude;
1293         dirChar = 'E';
1294     }
1295
1296     coordMin = (coord % 3600000) / 6;
1297     tncPrintf ("%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
1298     
1299 }
1300
1301 /**
1302  *   Generate the GPS NMEA-0183 $GPGGA packet.  Data is written through the tncTxByte
1303  *   callback function.
1304  */
1305 void tncGPGGAPacket()
1306 {
1307     // Generate the GPGGA message.
1308     tncPrintf ("$GPGGA,");
1309
1310     // Standard NMEA time.
1311     tncNMEATime();
1312
1313     // Standard NMEA-0183 latitude/longitude.
1314     tncNMEAFix();
1315
1316     // GPS status where 0: not available, 1: available
1317     if (gpsGetFixType() != GPS_NO_FIX)
1318         tncPrintf ("1,");
1319     else
1320         tncPrintf ("0,");
1321
1322     // Number of visible birds.
1323     tncPrintf ("%02d,", gpsPosition.trackedSats);
1324
1325     // DOP
1326     tncPrintf ("%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10);
1327
1328     // Altitude in meters.
1329     tncPrintf ("%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100));
1330
1331     // Checksum, we add 1 to skip over the $ character.
1332     tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
1333 }
1334
1335 /**
1336  *   Generate the GPS NMEA-0183 $GPRMC packet.  Data is written through the tncTxByte
1337  *   callback function.
1338  */
1339 void tncGPRMCPacket()
1340 {
1341     uint32_t temp;
1342
1343     // Generate the GPRMC message.
1344     tncPrintf ("$GPRMC,");
1345
1346     // Standard NMEA time.
1347     tncNMEATime();
1348
1349     // GPS status.
1350     if (gpsGetFixType() != GPS_NO_FIX)
1351         tncPrintf ("A,");
1352     else
1353         tncPrintf ("V,");
1354
1355     // Standard NMEA-0183 latitude/longitude.
1356     tncNMEAFix();
1357
1358     // Speed knots and heading.
1359     temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826;
1360     tncPrintf ("%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10);
1361
1362     // Date
1363     tncPrintf ("%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100);
1364
1365     // Checksum, skip over the $ character.
1366     tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
1367 }
1368
1369 /**
1370  *   Generate the plain text status packet.  Data is written through the tncTxByte
1371  *   callback function.
1372  */
1373 void tncStatusPacket(int16_t temperature)
1374 {
1375 //    uint16_t voltage;
1376
1377     // Plain text telemetry.
1378     tncPrintf (">ANSR ");
1379     
1380     // Display the flight time.
1381     tncPrintf ("%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds);
1382     
1383     // Altitude in feet.
1384     tncPrintf ("%ld' ", gpsPosition.altitudeFeet);
1385     
1386     // Peak altitude in feet.
1387     tncPrintf ("%ld'pk ", gpsGetPeakAltitude());
1388     
1389     // GPS hdop or pdop
1390     tncPrintf ("%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10);
1391
1392     // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix.
1393     switch (gpsGetFixType()) 
1394     {
1395         case GPS_NO_FIX:
1396             tncPrintf ("dop ");
1397             break;
1398
1399         case GPS_2D_FIX:
1400             tncPrintf ("hdop ");
1401             break;
1402
1403
1404         case GPS_3D_FIX:
1405             tncPrintf ("pdop ");
1406             break;
1407     } // END switch
1408
1409     // Number of satellites in the solution.
1410     tncPrintf ("%utrk ", gpsPosition.trackedSats);
1411     
1412     // Display main bus voltage.
1413 //    voltage = adcGetMainBusVolt();
1414 //    tncPrintf ("%lu.%02luvdc ", voltage / 100, voltage % 100);
1415     
1416     // Display internal temperature.
1417 //    tncPrintf ("%ld.%01ldF ", temperature / 10, abs(temperature % 10));
1418     
1419     // Print web address link.
1420     tncPrintf ("www.altusmetrum.org");
1421 }  
1422
1423 /** 
1424  *    Prepare an AX.25 data packet.  Each time this method is called, it automatically
1425  *    rotates through 1 of 3 messages.
1426  *
1427  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
1428  */
1429 void tncTxPacket(TNC_DATA_MODE dataMode)
1430 {
1431     int16_t temperature = 20;
1432     uint16_t crc;
1433
1434     // Only transmit if there is not another message in progress.
1435     if (tncMode != TNC_TX_READY)
1436         return;
1437
1438     // Configure the DDS for the desired operational.
1439     tncSetMode (dataMode);
1440
1441     // Set a pointer to our TNC output buffer.
1442     tncBufferPnt = tncBuffer;
1443
1444     // Set the message length counter.
1445     tncLength = 0;
1446
1447     // Determine the contents of the packet.
1448     switch (tncPacketType) 
1449     {
1450         case TNC_BOOT_MESSAGE:
1451             tncPrintf (">MegaMetrum v1.0 Beacon");
1452
1453             // Select the next packet we will generate.
1454             tncPacketType = TNC_STATUS;
1455             break;
1456
1457         case TNC_STATUS:
1458             tncStatusPacket(temperature);
1459
1460             // Select the next packet we will generate.
1461             tncPacketType = TNC_GGA;
1462             break;
1463
1464         case TNC_GGA:
1465             tncGPGGAPacket();
1466
1467             // Select the next packet we will generate.
1468             tncPacketType = TNC_RMC;
1469             break;
1470
1471         case TNC_RMC:
1472             tncGPRMCPacket();
1473
1474             // Select the next packet we will generate.
1475             tncPacketType = TNC_STATUS;
1476             break;
1477     }
1478
1479     // Add the end of message character.
1480     tncPrintf ("\015");
1481
1482     // Calculate the CRC for the header and message.
1483     crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
1484     crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
1485
1486     // Save the CRC in the message.
1487     *tncBufferPnt++ = crc & 0xff;
1488     *tncBufferPnt = (crc >> 8) & 0xff;
1489
1490     // Update the length to include the CRC bytes.
1491     tncLength += 2;
1492
1493     // Prepare the variables that are used in the real-time clock interrupt.
1494     tncBitCount = 0;
1495     tncShift = 0x7e;
1496     tncTxBit = 0;
1497     tncIndex = 0;
1498     tncMode = TNC_TX_SYNC;
1499
1500     // Turn on the PA chain.
1501 //    output_high (IO_PTT);
1502
1503     // Wait for the PA chain to power up.
1504 //    delay_ms (10);
1505
1506     // Key the DDS.
1507 //    output_high (IO_OSK);
1508
1509     // Log the battery and reference voltage just after we key the transmitter.
1510 //    sysLogVoltage();
1511     while (tncMode != TNC_TX_READY)
1512         timeUpdate();
1513 }
1514
1515 /** @} */