altos: Software implemenation of CC1111 radio encoding
authorKeith Packard <keithp@keithp.com>
Sun, 17 Jun 2012 23:17:00 +0000 (16:17 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 17 Jun 2012 23:17:00 +0000 (16:17 -0700)
Add CRC, whitening, FEC and interleaving routines for transmission
path to allow cc1120 to send telem packets to cc1111.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/.gitignore
src/Makefile
src/drivers/ao_cc1120.c
src/drivers/ao_cc1120_CC1120.h
src/drivers/ao_fec.h [new file with mode: 0644]
src/drivers/ao_fec_tx.c [new file with mode: 0644]
src/megametrum-v0.1/Makefile
src/test/Makefile
src/test/ao_fec_tx_test.c [new file with mode: 0644]
src/util/make-whiten [new file with mode: 0644]

index c6e1e3534d9d052b4d18e69d95d22bb1e3de60f6..cae36ae6a12a6d5a6363ab2e4d5ce38e6513021c 100644 (file)
@@ -1,2 +1,3 @@
 altitude.h
 altitude-pa.h
+ao_whiten.h
index c2e324c4074f4cf4e42639e20a6016c0af120dd3..3fca2ab259a9934c7e41dcdbd8f04c2bd09f1dbc 100644 (file)
@@ -6,6 +6,7 @@
 vpath make-altitude util
 vpath make-altitude-pa util
 vpath make-kalman util
+vpath make-whiten util
 vpath kalman.5c kalman
 vpath kalman_filter.5c kalman
 vpath load_csv.5c kalman
@@ -46,7 +47,7 @@ uninstall:
 
 all-recursive: all-local
 
-all-local: altitude.h altitude-pa.h ao_kalman.h
+all-local: altitude.h altitude-pa.h ao_kalman.h ao_whiten.h
 
 altitude.h: make-altitude
        nickle $< > $@
@@ -57,5 +58,8 @@ altitude-pa.h: make-altitude-pa
 ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c
        bash $< kalman > $@
 
+ao_whiten.h: make-whiten
+       nickle $< > $@
+
 clean-local:
        rm -f altitude.h ao_kalman.h
index f06d52ca162e761acc7aefef0d4086d58727587e..231d14d4357e9e803871dbc00a419a5a7722c6d8 100644 (file)
@@ -18,6 +18,7 @@
 #include <ao.h>
 #include <ao_cc1120.h>
 #include <ao_exti.h>
+#include <ao_fec.h>
 
 uint8_t ao_radio_wake;
 uint8_t ao_radio_mutex;
@@ -240,26 +241,27 @@ ao_radio_rx_done(void)
        return ao_radio_marc_status() == CC1120_MARC_STATUS1_RX_FINISHED;
 }
 
+static void
+ao_radio_start_tx(void)
+{
+       ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
+       ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+       ao_radio_strobe(CC1120_STX);
+}
+
 void
 ao_radio_rdf(uint8_t len)
 {
        int i;
 
        ao_radio_get(len);
-       ao_radio_abort = 0;
        ao_radio_wake = 0;
        for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
                ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
 
-       ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
-
        ao_radio_fifo_write_fixed(ao_radio_rdf_value, len);
 
-       ao_radio_reg_write(CC1120_PKT_LEN, len);
-
-       ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
-
-       ao_radio_strobe(CC1120_STX);
+       ao_radio_start_tx();
 
        cli();
        while (!ao_radio_wake && !ao_radio_abort)
@@ -327,24 +329,45 @@ ao_radio_test(void)
 void
 ao_radio_send(void *d, uint8_t size)
 {
-       uint8_t marc_status;
-
+       uint8_t         marc_status;
+       static uint8_t  prepare[128];
+       uint8_t         prepare_len;
+       static uint8_t  encode[256];
+       uint8_t         encode_len;
+       static uint8_t  interleave[256];
+       uint8_t         interleave_len;
+
+       ao_fec_dump_bytes(d, size, "Input");
+
+#if 1
+       prepare_len = ao_fec_prepare(d, size, prepare);
+       ao_fec_dump_bytes(prepare, prepare_len, "Prepare");
+
+       ao_fec_whiten(prepare, prepare_len, prepare);
+       ao_fec_dump_bytes(prepare, prepare_len, "Whiten");
+
+       encode_len = ao_fec_encode(prepare, prepare_len, encode);
+       ao_fec_dump_bytes(encode, encode_len, "Encode");
+
+       interleave_len = ao_fec_interleave(encode, encode_len, interleave);
+       ao_fec_dump_bytes(interleave, interleave_len, "Interleave");
+       ao_radio_get(interleave_len);
+       ao_radio_fifo_write(interleave, interleave_len);
+#else
        ao_radio_get(size);
-       ao_radio_wake = 0;
        ao_radio_fifo_write(d, size);
-       ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
-       ao_radio_strobe(CC1120_STX);
+#endif
+
+       ao_radio_wake = 0;
+
+       ao_radio_start_tx();
+
        cli();
-       for (;;) {
-               if (ao_radio_wake) {
-                       marc_status = ao_radio_marc_status();
-                       if (marc_status != CC1120_MARC_STATUS1_NO_FAILURE)
-                               break;
-                       ao_radio_wake = 0;
-               }
+       while (!ao_radio_wake && !ao_radio_abort)
                ao_sleep(&ao_radio_wake);
-       }
        sei();
+       if (!ao_radio_tx_done())
+               ao_radio_idle();
        ao_radio_put();
 }
 
@@ -361,6 +384,7 @@ ao_radio_recv(__xdata void *d, uint8_t size)
        cli();
        for (;;) {
                if (ao_radio_abort)
+
                        break;
                if (ao_radio_wake) {
                        marc_status = ao_radio_marc_status();
@@ -413,7 +437,7 @@ static const uint16_t packet_setup[] = {
        CC1120_DRATE0,          ((PACKET_DRATE_M >> 0) & 0xff),
        CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
                                 (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
-       CC1120_PKT_CFG1,        ((1 << CC1120_PKT_CFG1_WHITE_DATA) |
+       CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
                                 (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
                                 (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
                                 (1 << CC1120_PKT_CFG1_APPEND_STATUS)),
@@ -469,9 +493,6 @@ ao_radio_setup(void)
        for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
                ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
 
-       /* Enable marc status interrupt on gpio 2 pin */
-       ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP);
-
        ao_radio_set_packet();
 
        ao_config_get();
@@ -707,6 +728,21 @@ static void ao_radio_beep(void) {
        ao_radio_rdf(RDF_PACKET_LEN);
 }
 
+static void ao_radio_packet(void) {
+       static uint8_t packet[] = {
+#if 1
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+#else
+               3, 1, 2, 3
+#endif
+       };
+
+       ao_radio_send(packet, sizeof (packet));
+}
+
 #endif
 
 static const struct ao_cmds ao_radio_cmds[] = {
@@ -714,6 +750,7 @@ static const struct ao_cmds ao_radio_cmds[] = {
 #if CC1120_DEBUG
        { ao_radio_show,        "R\0Show CC1120 status" },
        { ao_radio_beep,        "b\0Emit an RDF beacon" },
+       { ao_radio_packet,      "p\0Send a test packet" },
 #endif
        { 0, NULL }
 };
index c900b7f9c2bb4aaa74f825a3db054305f91163aa..c0f35a23cc6e2c046c358db28e61258ff549046c 100644 (file)
  *\r
  ***************************************************************/\r
 \r
-        CC1120_SYNC3,                          0x93,       /* Sync Word Configuration [31:24] */\r
-        CC1120_SYNC2,                          0x0b,       /* Sync Word Configuration [23:16] */\r
-        CC1120_SYNC1,                          0x51,       /* Sync Word Configuration [15:8] */\r
-        CC1120_SYNC0,                          0xde,       /* Sync Word Configuration [7:0] */\r
+        CC1120_SYNC3,                          0xD3,       /* Sync Word Configuration [31:24] */\r
+        CC1120_SYNC2,                          0x91,       /* Sync Word Configuration [23:16] */\r
+        CC1120_SYNC1,                          0xD3,       /* Sync Word Configuration [15:8] */\r
+        CC1120_SYNC0,                          0x91,       /* Sync Word Configuration [7:0] */\r
+\r
         CC1120_SYNC_CFG1,                      0x08,       /* Sync Word Detection Configuration */\r
-        CC1120_SYNC_CFG0,                      0x17,       /* Sync Word Length Configuration */\r
-#if 0\r
-        CC1120_DEVIATION_M,                    0x50,       /* Frequency Deviation Configuration */\r
-        CC1120_MODCFG_DEV_E,                   0x0d,       /* Modulation Format and Frequency Deviation Configuration */\r
-#endif\r
+        CC1120_SYNC_CFG0,\r
+               (CC1120_SYNC_CFG0_SYNC_MODE_16_BITS << CC1120_SYNC_CFG0_SYNC_MODE) |\r
+               (CC1120_SYNC_CFG0_SYNC_NUM_ERROR_2 << CC1120_SYNC_CFG0_SYNC_NUM_ERROR),\r
         CC1120_DCFILT_CFG,                     0x1c,       /* Digital DC Removal Configuration */\r
-        CC1120_PREAMBLE_CFG1,                  0x18,       /* Preamble Length Configuration */\r
+        CC1120_PREAMBLE_CFG1,                             /* Preamble Length Configuration */\r
+               (CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES << CC1120_PREAMBLE_CFG1_NUM_PREAMBLE) |\r
+               (CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1120_PREAMBLE_CFG1_PREAMBLE_WORD),\r
         CC1120_PREAMBLE_CFG0,                  0x2a,       /*  */\r
         CC1120_FREQ_IF_CFG,                    0x40,       /* RX Mixer Frequency Configuration */\r
         CC1120_IQIC,                           0x46,       /* Digital Image Channel Compensation Configuration */\r
         CC1120_CHAN_BW,                        0x02,       /* Channel Filter Configuration */\r
+\r
         CC1120_MDMCFG1,                        0x46,       /* General Modem Parameter Configuration */\r
         CC1120_MDMCFG0,                        0x05,       /* General Modem Parameter Configuration */\r
-#if 0\r
-        CC1120_DRATE2,                         0x93,       /* Data Rate Configuration Exponent and Mantissa [19:16] */\r
-        CC1120_DRATE1,                         0xa4,       /* Data Rate Configuration Mantissa [15:8] */\r
-        CC1120_DRATE0,                         0x00,       /* Data Rate Configuration Mantissa [7:0] */\r
-#endif\r
+\r
         CC1120_AGC_REF,                        0x20,       /* AGC Reference Level Configuration */\r
         CC1120_AGC_CS_THR,                     0x19,       /* Carrier Sense Threshold Configuration */\r
         CC1120_AGC_GAIN_ADJUST,                0x00,       /* RSSI Offset Configuration */\r
diff --git a/src/drivers/ao_fec.h b/src/drivers/ao_fec.h
new file mode 100644 (file)
index 0000000..db5523a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FEC_H_
+#define _AO_FEC_H_
+
+#include <stdint.h>
+
+#define AO_FEC_CRC_INIT                        0xffff
+#define AO_FEC_TRELLIS_TERMINATOR      0x0b
+#define AO_FEC_PREPARE_EXTRA           4
+
+void
+ao_fec_dump_bytes(uint8_t *bytes, uint8_t len, char *name);
+
+uint16_t
+ao_fec_crc(uint8_t *bytes, uint8_t len);
+
+/*
+ * Append CRC and terminator bytes, returns resulting length.
+ * 'out' must be at least len + AO_FEC_PREPARE_EXTRA bytes long
+ */
+uint8_t
+ao_fec_prepare(uint8_t *in, uint8_t len, uint8_t *out);
+
+/*
+ * Whiten data using the cc1111 PN9 sequence. 'out'
+ * must be 'len' bytes long. 'out' and 'in' can be
+ * the same array
+ */
+uint8_t
+ao_fec_whiten(uint8_t *in, uint8_t len, uint8_t *out);
+
+/*
+ * Encode data. 'out' must be len*2 bytes long
+ */
+uint8_t
+ao_fec_encode(uint8_t *in, uint8_t len, uint8_t *out);
+
+/*
+ * Interleave data. 'out' must be 'len' bytes long
+ */
+uint8_t
+ao_fec_interleave(uint8_t *in, uint8_t len, uint8_t *out);
+
+#endif /* _AO_FEC_H_ */
diff --git a/src/drivers/ao_fec_tx.c b/src/drivers/ao_fec_tx.c
new file mode 100644 (file)
index 0000000..b893395
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_fec.h>
+#include <stdio.h>
+
+void
+ao_fec_dump_bytes(uint8_t *bytes, uint8_t len, char *name)
+{
+       uint8_t i;
+
+       printf ("%s (%d):", name, len);
+       for (i = 0; i < len; i++) {
+               if ((i & 7) == 0)
+                       printf ("\n\t%02x:", i);
+               printf(" %02x", bytes[i]);
+       }
+       printf ("\n");
+}
+
+static uint16_t inline
+crc_byte(uint8_t byte, uint16_t crc)
+{
+       uint8_t bit;
+
+       for (bit = 0; bit < 8; bit++) {
+               if (((crc & 0x8000) >> 8) ^ (byte & 0x80))
+                       crc = (crc << 1) ^ 0x8005;
+               else
+                       crc = (crc << 1);
+               byte <<= 1;
+       }
+       return crc;
+}
+
+uint16_t
+ao_fec_crc(uint8_t *bytes, uint8_t len)
+{
+       uint16_t        crc = AO_FEC_CRC_INIT;
+
+       while (len--)
+               crc = crc_byte(*bytes++, crc);
+       return crc;
+}
+
+uint8_t
+ao_fec_prepare(uint8_t *in, uint8_t len, uint8_t *out)
+{
+       uint16_t        crc = ao_fec_crc (in, len);
+       uint8_t         i;
+       uint8_t         num_fec;
+
+       /* Copy data */
+       for (i = 0; i < len; i++)
+               out[i] = in[i];
+
+       /* Append CRC */
+       out[i++] = crc >> 8;
+       out[i++] = crc;
+
+       /* Append FEC -- 1 byte if odd, two bytes if even */
+       num_fec = 2 - (i & 1);
+       while (num_fec--)
+               out[i++] = AO_FEC_TRELLIS_TERMINATOR;
+       return i;
+}
+
+static const uint8_t whiten[] = {
+#include "ao_whiten.h"
+};
+
+uint8_t
+ao_fec_whiten(uint8_t *in, uint8_t len, uint8_t *out)
+{
+       const uint8_t   *w = whiten;
+
+       while (len--)
+               *out++ = *in++ ^ *w++;
+}
+
+static const uint8_t ao_fec_encode_table[16] = {
+       0, 3, 1, 2,
+       3, 0, 2, 1,
+       3, 0, 2, 1,
+       0, 3, 1, 2
+};
+
+uint8_t
+ao_fec_encode(uint8_t *in, uint8_t len, uint8_t *out)
+{
+       uint16_t        fec = 0, output;
+       uint8_t         byte, bit;
+
+       for (byte = 0; byte < len; byte++) {
+               fec = (fec & 0x700) | in[byte];
+               output = 0;
+               for (bit = 0; bit < 8; bit++) {
+                       output = output << 2 | ao_fec_encode_table[fec >> 7];
+                       fec = (fec << 1) & 0x7ff;
+               }
+               out[byte * 2] = output >> 8;
+               out[byte * 2 + 1] = output;
+       }
+       return len * 2;
+}
+
+uint8_t
+ao_fec_interleave(uint8_t *in, uint8_t len, uint8_t *out)
+{
+       uint8_t i, j;
+
+       for (i = 0; i < len; i += 4) {
+               uint32_t        interleaved = 0;
+
+               for (j = 0; j < 4 * 4; j++) {
+                       interleaved <<= 2;
+                       interleaved |= (in[i + (~j & 0x3)] >> (2 * ((j & 0xc) >> 2))) & 0x03;
+               }
+               out[i+0] = interleaved >> 24;
+               out[i+1] = interleaved >> 16;
+               out[i+2] = interleaved >> 8;
+               out[i+3] = interleaved;
+       }
+       return len;
+}
index 5761fed751a32c020c49af9337e9d41941b96fa4..054855ba75b35e51f1138100f8cdee9dd8ebceca 100644 (file)
@@ -18,6 +18,8 @@ INC = \
        ao_ms5607.h \
        ao_hmc5883.h \
        ao_mpu6000.h \
+       ao_cc1120_CC1120.h \
+       ao_whiten.h \
        stm32l.h
 
 #
@@ -45,6 +47,7 @@ ALTOS_SRC = \
        ao_dma_stm.c \
        ao_spi_stm.c \
        ao_cc1120.c \
+       ao_fec_tx.c \
        ao_ms5607.c \
        ao_adc_stm.c \
        ao_beep_stm.c \
index 1e54f5e006933be08ba92d9d3333d61329f50f00..3e308dd7fe17ad02af1eecf246ccd590fa014f1f 100644 (file)
@@ -1,6 +1,6 @@
 vpath % ..:../core:../drivers:../util
 
-PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test
+PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_tx_test
 
 KALMAN=make-kalman 
 
@@ -38,4 +38,7 @@ ao_convert_pa_test: ao_convert_pa_test.c ao_convert_pa.c altitude-pa.h
        cc $(CFLAGS) -o $@ $<
 
 ao_kalman.h: $(KALMAN)
-       (cd .. && make ao_kalman.h)
\ No newline at end of file
+       (cd .. && make ao_kalman.h)
+
+ao_fec_tx_test: ao_fec_tx_test.c ao_fec_tx.c
+       cc $(CFLAGS) -o $@ ao_fec_tx_test.c ../drivers/ao_fec_tx.c
diff --git a/src/test/ao_fec_tx_test.c b/src/test/ao_fec_tx_test.c
new file mode 100644 (file)
index 0000000..b8dc930
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_fec.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+       uint8_t         input[4] = { 3, 1, 2, 3 };
+       uint8_t         prepare[sizeof(input) + AO_FEC_PREPARE_EXTRA];
+       uint8_t         encode[sizeof(prepare) * 2];
+       uint8_t         interleave[sizeof(encode)];
+       uint8_t         prepare_len;
+       uint8_t         encode_len;
+       uint8_t         interleave_len;
+
+       ao_fec_dump_bytes(input, sizeof(input), "Input");
+
+       prepare_len = ao_fec_prepare(input, sizeof (input), prepare);
+
+       ao_fec_dump_bytes(prepare, prepare_len, "Prepare");
+       
+       encode_len = ao_fec_encode(prepare, prepare_len, encode);
+
+       ao_fec_dump_bytes(encode, encode_len, "Encode");
+       
+       interleave_len = ao_fec_interleave(encode, encode_len, interleave);
+
+       ao_fec_dump_bytes(interleave, interleave_len, "Interleave");
+}
+
diff --git a/src/util/make-whiten b/src/util/make-whiten
new file mode 100644 (file)
index 0000000..d68a02a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/* Generate the data whitening array used by the CC11* radios */
+
+int pn9 = 0x1ff;
+
+void
+pn9_step() {
+       pn9 = (pn9 >> 1) | (((pn9 ^ (pn9 >> 5)) & 1) << 8);
+}
+
+int val()
+{
+       int     ret = pn9 & 0xff;
+
+       for (int i = 0; i < 8; i++)
+               pn9_step();
+       return ret;
+}
+
+for (int i = 0; i < 128; i++)
+       printf (" /* %3d */ 0x%02x,\n", i + 1, val());