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