/// 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; }
void radio_init() { Mirf.spi = &MirfHardwareSpi; Mirf.init(); Mirf.setRADDR((uint8_t *)RECEIVE_ADDRESS); Mirf.payload = sizeof(radio_packet_t); int channel = settings_get_channel(); radio_set_channel(channel, true); }
int cmd_handler_cooja(const uint8_t *data, int len) { if(data[0] == '!') { if(data[1] == 'C' && len == 3) { printf("cooja_cmd: setting channel: %d\n", data[2]); radio_set_channel(data[2]); //Enable radio NETSTACK_RDC.off(1); return 1; } else if(data[1] == 'M' && len == 10) { if(!linkaddr_cmp((linkaddr_t *)(data+2), &linkaddr_null)) { printf("cooja_cmd: Got MAC\n"); if(linkaddr_cmp(&default_link_addr, &linkaddr_null)) { linkaddr_copy(&default_link_addr, &linkaddr_node_addr); } memcpy(uip_lladdr.addr, data+2, sizeof(uip_lladdr.addr)); linkaddr_set_node_addr((linkaddr_t *) uip_lladdr.addr); } else { printf("cooja_cmd: Clear MAC disabled\n"); if(!linkaddr_cmp(&default_link_addr, &linkaddr_null)) { memcpy(uip_lladdr.addr, &default_link_addr, sizeof(uip_lladdr.addr)); linkaddr_set_node_addr(&default_link_addr); } //Stop radio NETSTACK_RDC.off(0); } return 1; } } else if(data[0] == '?') { if(data[1] == 'C' && len == 2) { uint8_t buf[4]; printf("cooja_cmd: getting channel: %d\n", data[2]); buf[0] = '!'; buf[1] = 'C'; buf[2] = 0; cmd_send(buf, 3); return 1; } } return 0; }
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(); }
/// main loop for time division multiplexing transparent serial /// void tdm_serial_loop(void) { __pdata uint16_t last_t = timer2_tick(); __pdata uint16_t last_link_update = last_t; _canary = 42; for (;;) { __pdata uint8_t len; __pdata uint16_t tnow, tdelta; __pdata uint8_t max_xmit; if (_canary != 42) { panic("stack blown\n"); } if (pdata_canary != 0x41) { panic("pdata canary changed\n"); } // give the AT command processor a chance to handle a command at_command(); // display test data if needed if (test_display) { display_test_output(); test_display = 0; } if (seen_mavlink && feature_mavlink_framing && !at_mode_active) { seen_mavlink = false; MAVLink_report(); } // set right receive channel radio_set_channel(fhop_receive_channel()); // get the time before we check for a packet coming in tnow = timer2_tick(); // see if we have received a packet if (radio_receive_packet(&len, pbuf)) { // update the activity indication received_packet = true; fhop_set_locked(true); // update filtered RSSI value and packet stats statistics.average_rssi = (radio_last_rssi() + 7*(uint16_t)statistics.average_rssi)/8; statistics.receive_count++; // we're not waiting for a preamble // any more transmit_wait = 0; if (len < 2) { // not a valid packet. We always send // two control bytes at the end of every packet continue; } // extract control bytes from end of packet memcpy(&trailer, &pbuf[len-sizeof(trailer)], sizeof(trailer)); len -= sizeof(trailer); if (trailer.window == 0 && len != 0) { // its a control packet if (len == sizeof(struct statistics)) { memcpy(&remote_statistics, pbuf, len); } // don't count control packets in the stats statistics.receive_count--; } else if (trailer.window != 0) { // sync our transmit windows based on // received header sync_tx_windows(len); last_t = tnow; if (trailer.command == 1) { handle_at_command(len); } else if (len != 0 && !packet_is_duplicate(len, pbuf, trailer.resend) && !at_mode_active) { // its user data - send it out // the serial port //printf("rcv(%d,[", len); LED_ACTIVITY = LED_ON; serial_write_buf(pbuf, len); LED_ACTIVITY = LED_OFF; //printf("]\n"); } } continue; } // see how many 16usec ticks have passed and update // the tdm state machine. We re-fetch tnow as a bad // packet could have cost us a lot of time. tnow = timer2_tick(); tdelta = tnow - last_t; tdm_state_update(tdelta); last_t = tnow; // update link status every 0.5s if (tnow - last_link_update > 32768) { link_update(); last_link_update = tnow; } if (lbt_rssi != 0) { // implement listen before talk if (radio_current_rssi() < lbt_rssi) { lbt_listen_time += tdelta; } else { lbt_listen_time = 0; if (lbt_rand == 0) { lbt_rand = ((uint16_t)rand()) % lbt_min_time; } } if (lbt_listen_time < lbt_min_time + lbt_rand) { // we need to listen some more continue; } } // we are allowed to transmit in our transmit window // or in the other radios transmit window if we have // bonus ticks #if USE_TICK_YIELD if (tdm_state != TDM_TRANSMIT && !(bonus_transmit && tdm_state == TDM_RECEIVE)) { // we cannot transmit now continue; } #else if (tdm_state != TDM_TRANSMIT) { continue; } #endif if (transmit_yield != 0) { // we've give up our window continue; } if (transmit_wait != 0) { // we're waiting for a preamble to turn into a packet continue; } if (!received_packet && radio_preamble_detected() || radio_receive_in_progress()) { // a preamble has been detected. Don't // transmit for a while transmit_wait = packet_latency; continue; } // sample the background noise when it is out turn to // transmit, but we are not transmitting, // averaged over around 4 samples statistics.average_noise = (radio_current_rssi() + 3*(uint16_t)statistics.average_noise)/4; if (duty_cycle_wait) { // we're waiting for our duty cycle to drop continue; } // how many bytes could we transmit in the time we // have left? if (tdm_state_remaining < packet_latency) { // none .... continue; } max_xmit = (tdm_state_remaining - packet_latency) / ticks_per_byte; if (max_xmit < PACKET_OVERHEAD) { // can't fit the trailer in with a byte to spare continue; } max_xmit -= PACKET_OVERHEAD; if (max_xmit > max_data_packet_length) { max_xmit = max_data_packet_length; } // ask the packet system for the next packet to send if (send_at_command && max_xmit >= strlen(remote_at_cmd)) { // send a remote AT command len = strlen(remote_at_cmd); memcpy(pbuf, remote_at_cmd, len); trailer.command = 1; send_at_command = false; } else { // get a packet from the serial port memset(pbuf, 0x40, 16); len = packet_get_next(max_xmit-16, pbuf+16) + 16; trailer.command = packet_is_injected(); if (len == 16) len = 0; } if (len > max_data_packet_length) { panic("oversized tdm packet"); } trailer.bonus = (tdm_state == TDM_RECEIVE); trailer.resend = packet_is_resend(); if (tdm_state == TDM_TRANSMIT && len == 0 && send_statistics && max_xmit >= sizeof(statistics)) { // send a statistics packet send_statistics = 0; memcpy(pbuf, &statistics, sizeof(statistics)); len = sizeof(statistics); // mark a stats packet with a zero window trailer.window = 0; trailer.resend = 0; } else { // calculate the control word as the number of // 16usec ticks that will be left in this // tdm state after this packet is transmitted trailer.window = (uint16_t)(tdm_state_remaining - flight_time_estimate(len+sizeof(trailer))); } // set right transmit channel radio_set_channel(fhop_transmit_channel()); memcpy(&pbuf[len], &trailer, sizeof(trailer)); if (len != 0 && trailer.window != 0) { // show the user that we're sending real data LED_ACTIVITY = LED_ON; } if (len == 0) { // sending a zero byte packet gives up // our window, but doesn't change the // start of the next window transmit_yield = 1; } // after sending a packet leave a bit of time before // sending the next one. The receivers don't cope well // with back to back packets transmit_wait = packet_latency; // if we're implementing a duty cycle, add the // transmit time to the number of ticks we've been transmitting if ((duty_cycle - duty_cycle_offset) != 100) { transmitted_ticks += flight_time_estimate(len+sizeof(trailer)); } // start transmitting the packet if (!radio_transmit(len + sizeof(trailer), pbuf, tdm_state_remaining + (silence_period/2)) && len != 0 && trailer.window != 0 && trailer.command == 0) { packet_force_resend(); } if (lbt_rssi != 0) { // reset the LBT listen time lbt_listen_time = 0; lbt_rand = 0; } // set right receive channel radio_set_channel(fhop_receive_channel()); // re-enable the receiver radio_receiver_on(); if (len != 0 && trailer.window != 0) { LED_ACTIVITY = LED_OFF; } } }