/// build the timing table static void tdm_build_timing_table(void) { __pdata uint8_t j; __pdata uint16_t rate; bool golay_saved = feature_golay; feature_golay = false; for (rate=2; rate<256; rate=(rate*3)/2) { __pdata uint32_t latency_sum=0, per_byte_sum=0; uint8_t size = MAX_PACKET_LENGTH; radio_configure(rate); for (j=0; j<50; j++) { __pdata uint16_t time_0, time_max, t1, t2; radio_set_channel(1); radio_receiver_on(); if (serial_read_available() > 0) { feature_golay = golay_saved; return; } t1 = timer2_tick(); if (!radio_transmit(0, pbuf, 0xFFFF)) { break; } t2 = timer2_tick(); radio_receiver_on(); time_0 = t2-t1; radio_set_channel(2); t1 = timer2_tick(); if (!radio_transmit(size, pbuf, 0xFFFF)) { if (size == 0) { break; } size /= 4; j--; continue; } t2 = timer2_tick(); radio_receiver_on(); time_max = t2-t1; latency_sum += time_0; per_byte_sum += ((size/2) + (time_max - time_0))/size; } if (j > 0) { printf("{ %u, %u, %u },\n", (unsigned)(radio_air_rate()), (unsigned)(latency_sum/j), (unsigned)(per_byte_sum/j)); } } feature_golay = golay_saved; }
static void radio_init(void) { __pdata uint32_t freq_min, freq_max; __pdata uint32_t channel_spacing; __pdata uint8_t txpower; // Do generic PHY initialisation if (!radio_initialise()) { panic("radio_initialise failed"); } switch (g_board_frequency) { case FREQ_433: freq_min = 433050000UL; freq_max = 434790000UL; txpower = 10; num_fh_channels = 10; break; case FREQ_470: freq_min = 470000000UL; freq_max = 471000000UL; txpower = 10; num_fh_channels = 10; break; case FREQ_868: freq_min = 868000000UL; freq_max = 869000000UL; txpower = 10; num_fh_channels = 10; break; case FREQ_915: freq_min = 915000000UL; freq_max = 928000000UL; txpower = 20; num_fh_channels = MAX_FREQ_CHANNELS; break; default: freq_min = 0; freq_max = 0; txpower = 0; panic("bad board frequency %d", g_board_frequency); break; } if (param_get(PARAM_NUM_CHANNELS) != 0) { num_fh_channels = param_get(PARAM_NUM_CHANNELS); } if (param_get(PARAM_MIN_FREQ) != 0) { freq_min = param_get(PARAM_MIN_FREQ) * 1000UL; } if (param_get(PARAM_MAX_FREQ) != 0) { freq_max = param_get(PARAM_MAX_FREQ) * 1000UL; } if (param_get(PARAM_TXPOWER) != 0) { txpower = param_get(PARAM_TXPOWER); } // constrain power and channels txpower = constrain(txpower, BOARD_MINTXPOWER, BOARD_MAXTXPOWER); num_fh_channels = constrain(num_fh_channels, 1, MAX_FREQ_CHANNELS); // double check ranges the board can do switch (g_board_frequency) { case FREQ_433: freq_min = constrain(freq_min, 414000000UL, 460000000UL); freq_max = constrain(freq_max, 414000000UL, 460000000UL); break; case FREQ_470: freq_min = constrain(freq_min, 450000000UL, 490000000UL); freq_max = constrain(freq_max, 450000000UL, 490000000UL); break; case FREQ_868: freq_min = constrain(freq_min, 849000000UL, 889000000UL); freq_max = constrain(freq_max, 849000000UL, 889000000UL); break; case FREQ_915: freq_min = constrain(freq_min, 868000000UL, 935000000UL); freq_max = constrain(freq_max, 868000000UL, 935000000UL); break; default: panic("bad board frequency %d", g_board_frequency); break; } if (freq_max == freq_min) { freq_max = freq_min + 1000000UL; } // get the duty cycle we will use duty_cycle = param_get(PARAM_DUTY_CYCLE); duty_cycle = constrain(duty_cycle, 0, 100); param_set(PARAM_DUTY_CYCLE, duty_cycle); // get the LBT threshold we will use lbt_rssi = param_get(PARAM_LBT_RSSI); if (lbt_rssi != 0) { // limit to the RSSI valid range lbt_rssi = constrain(lbt_rssi, 25, 220); } param_set(PARAM_LBT_RSSI, lbt_rssi); // sanity checks param_set(PARAM_MIN_FREQ, freq_min/1000); param_set(PARAM_MAX_FREQ, freq_max/1000); param_set(PARAM_NUM_CHANNELS, num_fh_channels); channel_spacing = (freq_max - freq_min) / (num_fh_channels+2); // add half of the channel spacing, to ensure that we are well // away from the edges of the allowed range freq_min += channel_spacing/2; // add another offset based on network ID. This means that // with different network IDs we will have much lower // interference srand(param_get(PARAM_NETID)); if (num_fh_channels > 5) { freq_min += ((unsigned long)(rand()*625)) % channel_spacing; } debug("freq low=%lu high=%lu spacing=%lu\n", freq_min, freq_min+(num_fh_channels*channel_spacing), channel_spacing); // set the frequency and channel spacing // change base freq based on netid radio_set_frequency(freq_min); // set channel spacing radio_set_channel_spacing(channel_spacing); // start on a channel chosen by network ID radio_set_channel(param_get(PARAM_NETID) % num_fh_channels); // And intilise the radio with them. if (!radio_configure(param_get(PARAM_AIR_SPEED)) && !radio_configure(param_get(PARAM_AIR_SPEED)) && !radio_configure(param_get(PARAM_AIR_SPEED))) { panic("radio_configure failed"); } // report the real air data rate in parameters param_set(PARAM_AIR_SPEED, radio_air_rate()); // setup network ID radio_set_network_id(param_get(PARAM_NETID)); // setup transmit power radio_set_transmit_power(txpower); // report the real transmit power in settings param_set(PARAM_TXPOWER, radio_get_transmit_power()); #ifdef USE_RTC // initialise real time clock rtc_init(); #endif // initialise frequency hopping system fhop_init(param_get(PARAM_NETID)); // initialise TDM system tdm_init(); }
// initialise the TDM subsystem void tdm_init(void) { __pdata uint16_t i; __pdata uint8_t air_rate = radio_air_rate(); __pdata uint32_t window_width; #define REGULATORY_MAX_WINDOW (((1000000UL/16)*4)/10) #define LBT_MIN_TIME_USEC 5000 // tdm_build_timing_table(); // calculate how many 16usec ticks it takes to send each byte ticks_per_byte = (8+(8000000UL/(air_rate*1000UL)))/16; ticks_per_byte++; // calculate the minimum packet latency in 16 usec units // we initially assume a preamble length of 40 bits, then // adjust later based on actual preamble length. This is done // so that if one radio has antenna diversity and the other // doesn't, then they will both using the same TDM round timings packet_latency = (8+(10/2)) * ticks_per_byte + 13; if (feature_golay) { max_data_packet_length = (MAX_PACKET_LENGTH/2) - (6+sizeof(trailer)); // golay encoding doubles the cost per byte ticks_per_byte *= 2; // and adds 4 bytes packet_latency += 4*ticks_per_byte; } else { max_data_packet_length = MAX_PACKET_LENGTH - sizeof(trailer); } // set the silence period to two times the packet latency silence_period = 2*packet_latency; // set the transmit window to allow for 3 full sized packets window_width = 3*(packet_latency+(max_data_packet_length*(uint32_t)ticks_per_byte)); // min listen time is 5ms lbt_min_time = LBT_MIN_TIME_USEC/16; // if LBT is enabled, we need at least 3*5ms of window width if (lbt_rssi != 0) { window_width = constrain(window_width, 3*lbt_min_time, window_width); } // the window width cannot be more than 0.4 seconds to meet US // regulations if (window_width >= REGULATORY_MAX_WINDOW && num_fh_channels > 1) { window_width = REGULATORY_MAX_WINDOW; } // user specified window is in milliseconds if (window_width > param_get(PARAM_MAX_WINDOW)*(1000/16)) { window_width = param_get(PARAM_MAX_WINDOW)*(1000/16); } // make sure it fits in the 13 bits of the trailer window if (window_width > 0x1fff) { window_width = 0x1fff; } tx_window_width = window_width; // now adjust the packet_latency for the actual preamble // length, so we get the right flight time estimates, while // not changing the round timings packet_latency += ((settings.preamble_length-10)/2) * ticks_per_byte; // tell the packet subsystem our max packet size, which it // needs to know for MAVLink packet boundary detection i = (tx_window_width - packet_latency) / ticks_per_byte; if (i > max_data_packet_length) { i = max_data_packet_length; } //TODO: decrease length here, test printf("setting max to %d\n", i); packet_set_max_xmit(i); // crc_test(); // tdm_test_timing(); // golay_test(); }