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