/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyright (c) 1999 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the MASH Research * Group at the University of California Berkeley. * 4. Neither the name of the University nor of the Research Group may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Contributed by Tom Henderson, UCB Daedalus Research Group, June 1999 */ #ifndef lint static const char rcsid[] = "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/satellite/sathandoff.cc,v 1.11 2005/08/22 05:08:34 tomh Exp $"; #endif #include "random.h" #include "sathandoff.h" #include "satlink.h" #include "satroute.h" #include "satposition.h" #include "satnode.h" #include "satgeometry.h" #include static class LinkHandoffMgrClass : public TclClass { public: LinkHandoffMgrClass() : TclClass("HandoffManager") {} TclObject* create(int, const char*const*) { return (new LinkHandoffMgr()); } } class_link_handoff_manager; static class SatLinkHandoffMgrClass : public TclClass { public: SatLinkHandoffMgrClass() : TclClass("HandoffManager/Sat") {} TclObject* create(int, const char*const*) { return (new SatLinkHandoffMgr()); } } class_sat_link_handoff_manager; static class TermLinkHandoffMgrClass : public TclClass { public: TermLinkHandoffMgrClass() : TclClass("HandoffManager/Term") {} TclObject* create(int, const char*const*) { return (new TermLinkHandoffMgr()); } } class_term_link_handoff_manager; void SatHandoffTimer::expire(Event*) { a_->handoff(); } void TermHandoffTimer::expire(Event*) { a_->handoff(); } ////////////////////////////////////////////////////////////////////////////// // class LinkHandoffMgr ////////////////////////////////////////////////////////////////////////////// RNG LinkHandoffMgr::handoff_rng_; int LinkHandoffMgr::handoff_randomization_ = 0; LinkHandoffMgr::LinkHandoffMgr() { bind_bool("handoff_randomization_", &handoff_randomization_); } int LinkHandoffMgr::command(int argc, const char*const* argv) { if (argc == 2) { } else if (argc == 3) { if(strcmp(argv[1], "setnode") == 0) { node_ = (Node*) TclObject::lookup(argv[2]); if (node_ == 0) return TCL_ERROR; return TCL_OK; } } return (TclObject::command(argc, argv)); } // Each crossseam satellite will have two net stacks-- at most one will // be occupied. This procedure finds an unoccupied stack on the node. SatLinkHead* LinkHandoffMgr::get_peer_next_linkhead(SatNode* np) { LinkHead* lhp; SatLinkHead* slhp; for (lhp = np->linklisthead().lh_first; lhp; lhp = lhp->nextlinkhead() ) { slhp = (SatLinkHead*) lhp; if (slhp->type() == LINK_ISL_CROSSSEAM) { if (!slhp->phy_tx()->channel() && !slhp->phy_rx()->channel() ) return slhp; } } printf("Error, couldn't find an empty crossseam stack for handoff\n"); return 0; } // This helper function assumes that the channel to which the link interface // is attached has one peer node (i.e., no other receive infs on channel) SatLinkHead* LinkHandoffMgr::get_peer_linkhead(SatLinkHead* slhp) { SatChannel *schan_; Phy *remote_phy_; Node *remote_node_; schan_ = (SatChannel*) slhp->phy_tx()->channel(); if (schan_ == 0) { printf("Error: get_peer_linkhead called for a non-"); printf("connected link on node %d\n", slhp->node()->address()); return 0; // Link is not currently connected } remote_phy_ = schan_->ifhead_.lh_first; if (remote_phy_ == 0) { printf("Error: node %d's tx phy ", slhp->node()->address()); printf("connected to channel with no receivers\n"); return 0; } remote_node_ = remote_phy_->head()->node(); if (remote_phy_->nextchnl()) { printf("Error: This ISL channel has more than one target\n"); return 0; } return ( (SatLinkHead*) remote_phy_->head()); } // This helper function assumes that the channel to which the link interface // is attached has one peer node (i.e., no other receive infs on channel) SatNode* LinkHandoffMgr::get_peer(SatLinkHead* slhp) { SatChannel *schan_; Phy *remote_phy_; schan_ = (SatChannel*) slhp->phy_tx()->channel(); if (schan_ == 0) return 0; // Link is not currently connected remote_phy_ = schan_->ifhead_.lh_first; if (remote_phy_ == 0) { // this is not an error as far as satellite GSL endpoints // appear to be concerned. // Commented out for drawing GSL links in dumpSats() // in satnode.cc // printf("Error: node %d's tx phy ", slhp->node()->address()); // printf("connected to channel with no receivers\n"); return 0; } if (remote_phy_->nextchnl()) { printf("Error: This ISL channel has more than one target\n"); return 0; } return ( (SatNode*) remote_phy_->head()->node()); } ////////////////////////////////////////////////////////////////////////// // class TermLinkHandoffMgr ////////////////////////////////////////////////////////////////////////// double TermLinkHandoffMgr::elevation_mask_ = 0; int TermLinkHandoffMgr::term_handoff_int_ = 10; TermLinkHandoffMgr::TermLinkHandoffMgr() : timer_(this) { bind("elevation_mask_", &elevation_mask_); bind("term_handoff_int_", &term_handoff_int_); } // // This is called each time the node checks to see if its link to a // polar satellite needs to be handed off. // There are two cases: // i) current link is up; check to see if it stays up or is handed off // ii) current link is down; check to see if it can go up // If there are any changes, call for rerouting. Finally, restart the timer. // int TermLinkHandoffMgr::handoff() { coordinate sat_coord, earth_coord; SatLinkHead* slhp; SatNode *peer_; // Polar satellite at opposite end of the GSL SatNode *best_peer_ = 0; // Best found peer for handoff Node *nodep; // Pointer used in searching the list of nodes PolarSatPosition *nextpos_; int link_changes_flag_ = FALSE; // Flag indicating change took place int restart_timer_flag_ = FALSE; // Restart timer only if polar links double found_elev_ = 0; //``Flag'' indicates whether handoff can occur double best_found_elev_ = 0; double mask_ = DEG_TO_RAD(TermLinkHandoffMgr::elevation_mask_); earth_coord = ((SatNode *)node_)->position()->coord(); // Traverse the linked list of link interfaces for (slhp = (SatLinkHead*) node_->linklisthead().lh_first; slhp; slhp = (SatLinkHead*) slhp->nextlinkhead() ) { if (slhp->type() == LINK_GSL_GEO || slhp->type() == LINK_GENERIC) continue; if (slhp->type() != LINK_GSL_POLAR) { printf("Error: Terminal link type "); printf("not valid %d NOW %f\n", slhp->type(), NOW); exit(1); } // The link is a GSL_POLAR link-- should be one receive // interface on it restart_timer_flag_ = TRUE; peer_ = get_peer(slhp); if (peer_) { sat_coord = peer_->position()->coord(); if (!SatGeometry::check_elevation(sat_coord, earth_coord, mask_) && slhp->linkup_) { slhp->linkup_ = FALSE; link_changes_flag_ = TRUE; // Detach receive link interface from channel // Next line removes phy from linked list // of interfaces attached to channel slhp->phy_rx()->removechnl(); // Set our channel pointers to NULL slhp->phy_tx()->setchnl(0); slhp->phy_rx()->setchnl(0); // wired-satellite integration if (SatRouteObject::instance().wiredRouting()) { Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", slhp->phy_tx()->node()->address(), peer_->address()); // Must do this bidirectionally Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", peer_->address(), slhp->phy_tx()->node()->address()); } } } if (!slhp->linkup_) { // If link is down, see if we can use another satellite // // As an optimization, first check the next satellite // coming over the horizon. Next, consider all // remaining satellites. // if (peer_) { // Next satellite nextpos_ = ((PolarSatPosition*) peer_->position())->next(); if (nextpos_) { sat_coord = nextpos_->coord(); found_elev_ = SatGeometry::check_elevation(sat_coord, earth_coord, mask_); if (found_elev_) peer_ = (SatNode*) nextpos_->node(); } } // Next, check all remaining satellites if not found if (!found_elev_) { for (nodep=Node::nodehead_.lh_first; nodep; nodep = nodep->nextnode()) { if (!SatNode::IsASatNode(nodep->address())) continue; peer_ = (SatNode*) nodep; if (peer_->position() && (peer_->position()->type() != POSITION_SAT_POLAR)) continue; sat_coord = peer_->position()->coord(); found_elev_ = SatGeometry::check_elevation(sat_coord, earth_coord, mask_); if (found_elev_ > best_found_elev_) { best_peer_ = peer_; best_found_elev_ = found_elev_; } } if (best_found_elev_ > 0.0) { assert (best_peer_ != 0); peer_ = best_peer_; found_elev_ = best_found_elev_; } } if (found_elev_) { slhp->linkup_ = TRUE; link_changes_flag_ = TRUE; // Point slhp->phy_tx to peer_'s inlink slhp->phy_tx()->setchnl(peer_->uplink()); // Point slhp->phy_rx to peer_'s outlink and // add phy_rx to the channels list of phy's slhp->phy_rx()->setchnl(peer_->downlink()); // Add phy to channel's linked list of i/fces slhp->phy_rx()->insertchnl(&(peer_->downlink()->ifhead_)); } } } if (link_changes_flag_) { SatRouteObject::instance().recompute(); } if (restart_timer_flag_) { // If we don't have polar GSLs, don't reset the timer if (handoff_randomization_) { timer_.resched(term_handoff_int_ + handoff_rng_.uniform(-1 * term_handoff_int_/2, term_handoff_int_/2)); } else timer_.resched(term_handoff_int_); } return link_changes_flag_; } ////////////////////////////////////////////////////////////////////////// // class SatLinkHandoffMgr ////////////////////////////////////////////////////////////////////////// double SatLinkHandoffMgr::latitude_threshold_ = 0; double SatLinkHandoffMgr::longitude_threshold_ = 0; int SatLinkHandoffMgr::sat_handoff_int_ = 10; SatLinkHandoffMgr::SatLinkHandoffMgr() : timer_(this) { bind("sat_handoff_int_", &sat_handoff_int_); bind("latitude_threshold_", &latitude_threshold_); bind("longitude_threshold_", &longitude_threshold_); } // // This function is responsible for activating, deactivating, and handing off // satellite ISLs. If the ISL is an intraplane link, // do nothing. If the ISL is an interplane link, it will be taken down // when _either_ of the connected satellites are above lat_threshold_ // degrees, and brought back up when _both_ satellites move below // lat_threshold_ again. If an ISL is a cross-seam link, it must also be // handed off periodically while the satellite is below lat_threshold_. // // Finally, optimizations that avoid going through the linked lists unless // the satellite is ``close'' to lat_threshold_ are employed. // int SatLinkHandoffMgr::handoff() { SatLinkHead *slhp, *peer_slhp, *peer_next_slhp; SatNode *local_, *peer_, *peer_next_; PolarSatPosition *pos_, *peer_pos_, *peer_next_pos_; double dist_to_peer, dist_to_next; Channel *tx_channel_, *rx_channel_; double sat_latitude_, sat_longitude_, peer_latitude_, peer_longitude_; int link_down_flag_; double lat_threshold_ = DEG_TO_RAD(latitude_threshold_); double cross_long_threshold_ = DEG_TO_RAD(longitude_threshold_); int link_changes_flag_ = FALSE; // Flag indicating change took place coordinate local_coord_, peer_coord_; local_ = (SatNode*) node_; local_coord_ = local_->position()->coord(); sat_latitude_ = SatGeometry::get_latitude(local_->position()->coord()); sat_longitude_= SatGeometry::get_longitude(local_->position()->coord()); // First go through crossseam ISLs to search for handoffs for (slhp = (SatLinkHead*) local_->linklisthead().lh_first; slhp; slhp = (SatLinkHead*) slhp->nextlinkhead() ) { if (slhp->type() != LINK_ISL_CROSSSEAM) continue; peer_ = get_peer(slhp); if (peer_ == 0) continue; // this link interface is not attached // If this is a crossseam link, first see if the link must // be physically handed off to the next satellite. // Handoff is needed if the satellite at the other end of // the link is further away than the ``next'' satellite // in the peer's orbital plane. pos_ = (PolarSatPosition*)slhp->node()->position(); peer_slhp = get_peer_linkhead(slhp); peer_pos_ = (PolarSatPosition*) peer_->position(); peer_coord_ = peer_pos_->coord(); if (fabs(sat_latitude_) > lat_threshold_) link_down_flag_ = TRUE; else link_down_flag_ = FALSE; if (peer_pos_->plane() < pos_->plane()) { // Crossseam handoff is controlled by satellites // in the plane with a lower index break; } peer_next_pos_ = peer_pos_->next(); if (!peer_next_pos_) { printf("Error: crossseam handoffs require "); printf("setting the ``next'' field\n"); exit(1); } peer_next_ = (SatNode*) peer_next_pos_->node(); dist_to_peer = SatGeometry::distance(peer_coord_, local_coord_); dist_to_next = SatGeometry::distance(peer_next_pos_->coord(), local_coord_); if (dist_to_next < dist_to_peer) { // Handoff -- the "next" satellite should have a // currently unused network stack. Find this // unused stack and handoff the channels to it. // // Remove peer's tx/rx interface from channel peer_slhp->phy_rx()->removechnl(); peer_slhp->phy_tx()->setchnl(0); peer_slhp->phy_rx()->setchnl(0); // Add peer_next's tx/rx interfaces to our channels peer_next_slhp = get_peer_next_linkhead(peer_next_); tx_channel_ = slhp->phy_tx()->channel(); rx_channel_ = slhp->phy_rx()->channel(); peer_next_slhp->phy_tx()->setchnl(rx_channel_); peer_next_slhp->phy_rx()->setchnl(tx_channel_); peer_next_slhp->phy_rx()->insertchnl(&(tx_channel_->ifhead_)); link_changes_flag_ = TRUE; // wired-satellite integration if (SatRouteObject::instance().wiredRouting()) { // Check if link is up first before deleting if (slhp->linkup_) { Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", slhp->phy_tx()->node()->address(), peer_->address()); } if (peer_slhp->linkup_) { Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", peer_->address(), slhp->phy_tx()->node()->address()); } } // Now reset the peer_ variables to point to next peer_ = peer_next_; peer_slhp = peer_next_slhp; peer_coord_ = peer_->position()->coord(); } // Next, see if the link needs to be taken down. peer_latitude_ = SatGeometry::get_latitude(peer_coord_); peer_longitude_ = SatGeometry::get_longitude(peer_coord_); if (fabs(peer_latitude_) > lat_threshold_) link_down_flag_ = TRUE; // If the two satellites are too close to each other in // longitude, the link should be down if ((fabs(peer_longitude_ - sat_longitude_) < cross_long_threshold_) || fabs(peer_longitude_ - sat_longitude_) > (2 * PI - cross_long_threshold_)) link_down_flag_ = TRUE; // Check to see if link grazes atmosphere at an altitude // below the atmospheric margin link_down_flag_ |= !(SatGeometry::are_satellites_mutually_visible(peer_coord_, local_coord_)); // Evaluate whether a change in link status is needed if ((slhp->linkup_ || peer_slhp->linkup_) && link_down_flag_) { slhp->linkup_ = FALSE; peer_slhp->linkup_ = FALSE; link_changes_flag_ = TRUE; // wired-satellite integration if (SatRouteObject::instance().wiredRouting()) { Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", slhp->phy_tx()->node()->address(), peer_->address()); Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", peer_->address(), slhp->phy_tx()->node()->address()); } } else if ((!slhp->linkup_ || !peer_slhp->linkup_) && !link_down_flag_) { slhp->linkup_ = TRUE; peer_slhp->linkup_ = TRUE; link_changes_flag_ = TRUE; } } // Now, work on interplane ISLs (intraplane ISLs are not handed off) // Now search for interplane ISLs for (slhp = (SatLinkHead*) local_->linklisthead().lh_first; slhp; slhp = (SatLinkHead*) slhp->nextlinkhead() ) { if (slhp->type() != LINK_ISL_INTERPLANE) continue; if (fabs(sat_latitude_) > lat_threshold_) link_down_flag_ = TRUE; else link_down_flag_ = FALSE; peer_ = get_peer(slhp); peer_slhp = get_peer_linkhead(slhp); peer_coord_ = peer_->position()->coord(); peer_latitude_ = SatGeometry::get_latitude(peer_coord_); if (fabs(peer_latitude_) > lat_threshold_) link_down_flag_ = TRUE; link_down_flag_ |= !(SatGeometry::are_satellites_mutually_visible(peer_coord_, local_coord_)); if (slhp->linkup_ && link_down_flag_) { // Take links down if either satellite at high latitude slhp->linkup_ = FALSE; peer_slhp->linkup_ = FALSE; link_changes_flag_ = TRUE; // wired-satellite integration if (SatRouteObject::instance().wiredRouting()) { Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", slhp->phy_tx()->node()->address(), peer_->address()); Tcl::instance().evalf("[Simulator instance] sat_link_destroy %d %d", peer_->address(), slhp->phy_tx()->node()->address()); } } else if (!slhp->linkup_ && !link_down_flag_) { slhp->linkup_ = TRUE; peer_slhp->linkup_ = TRUE; link_changes_flag_ = TRUE; } } if (link_changes_flag_) { SatRouteObject::instance().recompute(); } if (handoff_randomization_) { timer_.resched(sat_handoff_int_ + handoff_rng_.uniform(-1 * sat_handoff_int_/2, sat_handoff_int_/2)); } else timer_.resched(sat_handoff_int_); return link_changes_flag_; }