c1a800a9211a49131492b6b709c93d2d272fe746
[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 void ddsInit();
156 void ddsSetAmplitude (uint8_t amplitude);
157 void ddsSetOutputScale (uint16_t amplitude);
158 void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
159 void ddsSetFreq (uint32_t freq);
160 void ddsSetFTW (uint32_t ftw);
161
162 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
163
164 uint8_t timeGetTicks();
165 void timeInit();
166 void timeSetDutyCycle (uint8_t dutyCycle);
167 void timeUpdate();
168
169 void tncInit();
170 bool_t tncIsFree();
171 void tncHighRate(bool_t state);
172 void tnc1200TimerTick();
173 void tncTxByte (uint8_t value);
174 void tncTxPacket(void);
175
176 /** @} */
177
178 /**
179  *  @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
180  *
181  *  Functions to control the Analog Devices AD9954 DDS.
182  *
183  *  @{
184  */
185
186 /// Number of digits in DDS frequency to FTW conversion.
187 #define DDS_FREQ_TO_FTW_DIGITS 9
188
189 /// Array of multiplication factors used to convert frequency to the FTW.
190 const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
191
192 /// Array of divisors used to convert frequency to the FTW.
193 const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
194
195 /// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
196 const uint16_t DDS_AMP_TO_SCALE[] = 
197
198     16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, 
199     8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, 
200     3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 
201 };
202
203
204 /// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz  (445.950MHz)
205 const uint32_t freqTable[256] = 
206 {
207     955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, 
208     955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, 
209     955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, 
210     955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, 
211     955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, 
212     955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, 
213     955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, 
214     955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, 
215     955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, 
216     955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, 
217     955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, 
218     955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, 
219     955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, 
220     955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, 
221     955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, 
222     955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, 
223     955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, 
224     955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, 
225     955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, 
226     955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, 
227     955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, 
228     955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, 
229     955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, 
230     955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, 
231     955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, 
232     955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, 
233     955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, 
234     955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, 
235     955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, 
236     955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, 
237     955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, 
238     955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
239 };
240
241 /**
242  *  Set DDS frequency tuning word.  The output frequency is equal to RefClock * (ftw / 2 ^ 32).
243  *
244  *  @param ftw Frequency Tuning Word
245  */
246 void ddsSetFTW (uint32_t ftw)
247 {
248     int x = ftw - freqTable[0];
249     putchar (x > 0 ? 0xc0 : 0x40);
250 }
251
252 /** @} */
253
254 /**
255  *  @defgroup sys System Library Functions
256  *
257  *  Generic system functions similiar to the run-time C library.
258  *
259  *  @{
260  */
261
262 /**
263  *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
264  *    The crc parameter allow the calculation on the CRC on multiple buffers.
265  *
266  *    @param buffer Pointer to data buffer.
267  *    @param length number of bytes in data buffer
268  *    @param crc starting value
269  *
270  *    @return CRC-16 of buffer[0 .. length]
271  */
272 uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
273 {
274     uint8_t i, bit, value;
275
276     for (i = 0; i < length; ++i) 
277     {
278         value = buffer[i];
279
280         for (bit = 0; bit < 8; ++bit) 
281         {
282             crc ^= (value & 0x01);
283             crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
284             value = value >> 1;
285         } // END for
286     } // END for
287
288     return crc ^ 0xffff;
289 }
290
291 /** @} */
292
293 /**
294  *  @defgroup rtc Real Time Interrupt tick
295  *
296  *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
297  *
298  *  @{
299  */
300
301 /// A counter that ticks every 100mS.
302 uint8_t timeTicks;
303
304 /// Counts the number of 104uS interrupts for a 100mS time period.
305 uint16_t timeInterruptCount;
306
307 /// Counts the number of 100mS time periods in 1 second.
308 uint8_t time100ms;
309
310 /// System time in seconds.
311 uint8_t timeSeconds;
312
313 /// System time in minutes.
314 uint8_t timeMinutes;
315
316 /// System time in hours.
317 uint8_t timeHours;
318
319 /// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
320 uint8_t timeDutyCycle;
321
322 /// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
323 uint16_t timeCompare;
324
325 /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
326 uint16_t timeNCO;
327
328 /// Audio tone NCO update step (phase).
329 uint16_t timeNCOFreq;
330
331 /// Counter used to deciminate down from the 104uS to 833uS interrupt rate.  (9600 to 1200 baud) 
332 uint8_t timeLowRateCount;
333
334 /// Flag set true once per second.
335 bool_t timeUpdateFlag;
336
337 /// Flag that indicate the flight time should run.
338 bool_t timeRunFlag;
339
340 /// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
341 #define TIME_RATE 125
342
343 /**
344  *   Running 8-bit counter that ticks every 100mS.
345  *
346  *   @return 100mS time tick
347  */
348 uint8_t timeGetTicks()
349 {
350     return timeTicks;
351 }
352
353 /**
354  *   Initialize the real-time clock.
355  */
356 void timeInit()
357 {
358     timeTicks = 0;
359     timeInterruptCount = 0;
360 //    time100mS = 0;
361     timeSeconds = 0;
362     timeMinutes = 0;
363     timeHours = 0;
364     timeCompare = TIME_RATE;
365     timeUpdateFlag = false;
366     timeNCO = 0x00;
367     timeLowRateCount = 0;
368     timeNCOFreq = 0x2000;
369     timeRunFlag = false;
370 }
371
372 /**
373  *   Function return true once a second based on real-time clock.
374  *
375  *   @return true on one second tick; otherwise false
376  */
377 bool_t timeIsUpdate()
378 {
379     if (timeUpdateFlag) 
380     {
381         timeUpdateFlag = false;
382         return true;
383     } // END if
384
385     return false;
386 }
387
388 /**
389  *   Set a flag to indicate the flight time should run.  This flag is typically set when the payload
390  *   lifts off.
391  */
392 void timeSetRunFlag()
393 {
394     timeRunFlag = true;
395 }
396
397 /**
398  *   Timer interrupt handler called every 104uS (9600 times/second).
399  */
400 void timeUpdate()
401 {
402     // Setup the next interrupt for the operational mode.
403     timeCompare += TIME_RATE;
404
405     ddsSetFTW (freqTable[timeNCO >> 8]);
406
407     timeNCO += timeNCOFreq;
408
409     if (++timeLowRateCount == 8) 
410     {
411         timeLowRateCount = 0;
412         tnc1200TimerTick();
413     } // END if
414 }
415
416 /** @} */
417
418 /**
419  *  @defgroup tnc TNC (Terminal Node Controller)
420  *
421  *  Functions that provide a subset of the TNC functions.
422  *
423  *  @{
424  */
425
426 /// The number of start flag bytes to send before the packet message.  (360bits * 1200bps = 300mS)
427 #define TNC_TX_DELAY 45
428
429 /// The size of the TNC output buffer.
430 #define TNC_BUFFER_SIZE 80
431
432 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
433 typedef enum
434 {
435     /// Stand by state ready to accept new message.
436     TNC_TX_READY,
437
438     /// 0x7E bit stream pattern used to define start of APRS message.
439     TNC_TX_SYNC,
440
441     /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags.
442     TNC_TX_HEADER,
443
444     /// Transmit the message data.
445     TNC_TX_DATA,
446
447     /// Transmit the end flag sequence.
448     TNC_TX_END
449 } TNC_TX_1200BPS_STATE;
450
451 /// Enumeration of the messages we can transmit. 
452 typedef enum
453 {
454     /// Startup message that contains software version information.
455     TNC_BOOT_MESSAGE,
456
457     /// Plain text status message.
458     TNC_STATUS,
459
460     /// Message that contains GPS NMEA-0183 $GPGGA message.
461     TNC_GGA,
462
463     /// Message that contains GPS NMEA-0183 $GPRMC message.
464     TNC_RMC
465 }  TNC_MESSAGE_TYPE;
466
467 /// AX.25 compliant packet header that contains destination, station call sign, and path.
468 /// 0x76 for SSID-11, 0x78 for SSID-12
469 uint8_t TNC_AX25_HEADER[30] = { 
470     'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
471     'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \
472     'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
473     'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
474     0x03, 0xf0 };
475
476
477 /// The next bit to transmit.
478 uint8_t tncTxBit;
479
480 /// Current mode of the 1200 bps state machine.
481 TNC_TX_1200BPS_STATE tncMode;
482
483 /// Counter for each bit (0 - 7) that we are going to transmit.
484 uint8_t tncBitCount;
485
486 /// A shift register that holds the data byte as we bit shift it for transmit.
487 uint8_t tncShift;
488
489 /// Index into the APRS header and data array for each byte as we transmit it.
490 uint8_t tncIndex;
491
492 /// The number of bytes in the message portion of the AX.25 message.
493 uint8_t tncLength;
494
495 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
496 uint8_t tncBitStuff;
497
498 /// Pointer to TNC buffer as we save each byte during message preparation.
499 uint8_t *tncBufferPnt;
500
501 /// The type of message to tranmit in the next packet.
502 TNC_MESSAGE_TYPE tncPacketType;
503
504 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
505 uint8_t tncBuffer[TNC_BUFFER_SIZE];
506
507 /// Flag that indicates we want to transmit every 5 seconds.
508 bool_t tncHighRateFlag;
509
510 /** 
511  *   Initialize the TNC internal variables.
512  */
513 void tncInit()
514 {
515     tncTxBit = 0;
516     tncMode = TNC_TX_READY;
517     tncPacketType = TNC_BOOT_MESSAGE;
518     tncHighRateFlag = false;
519 }
520
521 /**
522  *  Determine if the hardware if ready to transmit a 1200 baud packet.
523  *
524  *  @return true if ready; otherwise false
525  */
526 bool_t tncIsFree()
527 {
528     if (tncMode == TNC_TX_READY)
529         return true;
530
531     return false;
532 }
533
534 void tncHighRate(bool_t state)
535 {
536     tncHighRateFlag = state;
537 }
538
539 /**
540  *  Determine if the seconds value timeSeconds is a valid time slot to transmit
541  *  a message.  Time seconds is in UTC.
542  *
543  *  @param timeSeconds UTC time in seconds
544  *
545  *  @return true if valid time slot; otherwise false
546  */
547 bool_t tncIsTimeSlot (uint8_t timeSeconds)
548 {
549     if (tncHighRateFlag)
550     {
551         if ((timeSeconds % 5) == 0)
552             return true;
553
554         return false;
555     } // END if
556
557     switch (timeSeconds) 
558     {
559         case 0:
560         case 15:
561         case 30:
562         case 45:
563             return true;
564
565         default:
566             return false;
567     } // END switch
568 }
569
570 /**
571  *   Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
572  *   The provides the pre and postamble as well as the bit stuffed data stream.
573  */
574 void tnc1200TimerTick()
575 {
576     // Set the A-FSK frequency.
577     if (tncTxBit == 0x00)
578         timeNCOFreq = 0x2000;
579     else
580         timeNCOFreq = 0x3aab;
581
582     switch (tncMode) 
583     {
584         case TNC_TX_READY:
585             // Generate a test signal alteranting between high and low tones.
586             tncTxBit = (tncTxBit == 0 ? 1 : 0);
587             break;
588
589         case TNC_TX_SYNC:
590             // The variable tncShift contains the lastest data byte.
591             // NRZI enocde the data stream.
592             if ((tncShift & 0x01) == 0x00) {
593                 if (tncTxBit == 0)
594                     tncTxBit = 1;
595                 else
596                     tncTxBit = 0;
597             }
598                     
599             // When the flag is done, determine if we need to send more or data.
600             if (++tncBitCount == 8) 
601             {
602                 tncBitCount = 0;
603                 tncShift = 0x7e;
604
605                 // Once we transmit x mS of flags, send the data.
606                 // txDelay bytes * 8 bits/byte * 833uS/bit = x mS
607                 if (++tncIndex == TNC_TX_DELAY) 
608                 {
609                     tncIndex = 0;
610                     tncShift = TNC_AX25_HEADER[0];
611                     tncBitStuff = 0;
612                     tncMode = TNC_TX_HEADER;
613                 } // END if
614             } else
615                 tncShift = tncShift >> 1;
616             break;
617
618         case TNC_TX_HEADER:
619             // Determine if we have sent 5 ones in a row, if we have send a zero.
620             if (tncBitStuff == 0x1f) 
621             {
622                 if (tncTxBit == 0)
623                     tncTxBit = 1;
624                 else
625                     tncTxBit = 0;
626
627                 tncBitStuff = 0x00;
628                 return;
629             }    // END if
630
631             // The variable tncShift contains the lastest data byte.
632             // NRZI enocde the data stream.
633             if ((tncShift & 0x01) == 0x00) {
634                 if (tncTxBit == 0)
635                     tncTxBit = 1;
636                 else
637                     tncTxBit = 0;
638             }
639
640             // Save the data stream so we can determine if bit stuffing is 
641             // required on the next bit time.
642             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
643
644             // If all the bits were shifted, get the next byte.
645             if (++tncBitCount == 8) 
646             {
647                 tncBitCount = 0;
648
649                 // After the header is sent, then send the data.
650                 if (++tncIndex == sizeof(TNC_AX25_HEADER)) 
651                 {
652                     tncIndex = 0;
653                     tncShift = tncBuffer[0];
654                     tncMode = TNC_TX_DATA;
655                 } else
656                     tncShift = TNC_AX25_HEADER[tncIndex];
657
658             } else
659                 tncShift = tncShift >> 1;
660
661             break;
662
663         case TNC_TX_DATA:
664             // Determine if we have sent 5 ones in a row, if we have send a zero.
665             if (tncBitStuff == 0x1f) 
666             {
667                 if (tncTxBit == 0)
668                     tncTxBit = 1;
669                 else
670                     tncTxBit = 0;
671
672                 tncBitStuff = 0x00;
673                 return;
674             }    // END if
675
676             // The variable tncShift contains the lastest data byte.
677             // NRZI enocde the data stream.
678             if ((tncShift & 0x01) == 0x00) {
679                 if (tncTxBit == 0)
680                     tncTxBit = 1;
681                 else
682                     tncTxBit = 0;
683             }
684
685             // Save the data stream so we can determine if bit stuffing is 
686             // required on the next bit time.
687             tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
688
689             // If all the bits were shifted, get the next byte.
690             if (++tncBitCount == 8) 
691             {
692                 tncBitCount = 0;
693
694                 // If everything was sent, transmit closing flags.
695                 if (++tncIndex == tncLength) 
696                 {
697                     tncIndex = 0;
698                     tncShift = 0x7e;
699                     tncMode = TNC_TX_END;
700                 } else
701                     tncShift = tncBuffer[tncIndex];
702
703             } else
704                 tncShift = tncShift >> 1;
705
706             break;
707
708         case TNC_TX_END:
709             // The variable tncShift contains the lastest data byte.
710             // NRZI enocde the data stream. 
711             if ((tncShift & 0x01) == 0x00) {
712                 if (tncTxBit == 0)
713                     tncTxBit = 1;
714                 else
715                     tncTxBit = 0;
716             }
717
718             // If all the bits were shifted, get the next one.
719             if (++tncBitCount == 8) 
720             {
721                 tncBitCount = 0;
722                 tncShift = 0x7e;
723     
724                 // Transmit two closing flags.
725                 if (++tncIndex == 2) 
726                 {
727                     tncMode = TNC_TX_READY;
728
729                     return;
730                 } // END if
731             } else
732                 tncShift = tncShift >> 1;
733
734             break;
735     } // END switch
736 }
737
738 /**
739  *   Method that is called every 104uS to transmit the 9600bps FSK data stream.
740  */
741 void tnc9600TimerTick()
742 {
743
744 }
745
746 /**
747  *   Generate the plain text position packet. Data is written through the tncTxByte
748  *   callback function
749  */
750 void tncPositionPacket(void)
751 {
752     int32_t     latitude = 45.4694766 * 10000000;
753     int32_t     longitude = -122.7376250 * 10000000;
754     uint32_t    altitude = 10000;
755     uint16_t    lat_deg;
756     uint16_t    lon_deg;
757     uint16_t    lat_min;
758     uint16_t    lat_frac;
759     uint16_t    lon_min;
760     uint16_t    lon_frac;
761     int         c;
762
763     char        lat_sign = 'N', lon_sign = 'E';
764
765     if (latitude < 0) {
766         lat_sign = 'S';
767         latitude = -latitude;
768     }
769
770     if (longitude < 0) {
771         lon_sign = 'W';
772         longitude = -longitude;
773     }
774
775     lat_deg = latitude / 10000000;
776     latitude -= lat_deg * 10000000;
777     latitude *= 60;
778     lat_min = latitude / 10000000;
779     latitude -= lat_min * 10000000;
780     lat_frac = (latitude + 50000) / 100000;
781
782     lon_deg = longitude / 10000000;
783     longitude -= lon_deg * 10000000;
784     longitude *= 60;
785     lon_min = longitude / 10000000;
786     longitude -= lon_min * 10000000;
787     lon_frac = (longitude + 50000) / 100000;
788
789     c = sprintf ((char *) tncBufferPnt, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015",
790                 lat_deg, lat_min, lat_frac, lat_sign,
791                 lon_deg, lon_min, lon_frac, lon_sign,
792                 altitude * 100 / 3048);
793     tncBufferPnt += c;
794     tncLength += c;
795 }
796
797 /** 
798  *    Prepare an AX.25 data packet.  Each time this method is called, it automatically
799  *    rotates through 1 of 3 messages.
800  *
801  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
802  */
803 void tncTxPacket(void)
804 {
805     uint16_t crc;
806
807     // Set a pointer to our TNC output buffer.
808     tncBufferPnt = tncBuffer;
809
810     // Set the message length counter.
811     tncLength = 0;
812
813     tncPositionPacket();
814
815     // Calculate the CRC for the header and message.
816     crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
817     crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
818
819     // Save the CRC in the message.
820     *tncBufferPnt++ = crc & 0xff;
821     *tncBufferPnt = (crc >> 8) & 0xff;
822
823     // Update the length to include the CRC bytes.
824     tncLength += 2;
825
826     // Prepare the variables that are used in the real-time clock interrupt.
827     tncBitCount = 0;
828     tncShift = 0x7e;
829     tncTxBit = 0;
830     tncIndex = 0;
831     tncMode = TNC_TX_SYNC;
832
833     // Turn on the PA chain.
834 //    output_high (IO_PTT);
835
836     // Wait for the PA chain to power up.
837 //    delay_ms (10);
838
839     // Key the DDS.
840 //    output_high (IO_OSK);
841
842     // Log the battery and reference voltage just after we key the transmitter.
843 //    sysLogVoltage();
844     while (tncMode != TNC_TX_READY)
845         timeUpdate();
846 }
847
848 /** @} */