Subversion Repositories Projects

Rev

Blame | Last modification | View Log | RSS feed

#!/usr/bin/perl
#!/usr/bin/perl -d:ptkdb

###############################################################################
#
# libmap.pl -  Conversion GPS and Map-X/Y Coordinates
#
# Copyright (C) 2009  Rainer Walther  (rainerwalther-mail@web.de)
#
# Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
#
# Es ist Ihnen gestattet:
#     * das Werk vervielfältigen, verbreiten und öffentlich zugänglich machen
#     * Abwandlungen bzw. Bearbeitungen des Inhaltes anfertigen
#
# Zu den folgenden Bedingungen:
#     * Namensnennung.
#       Sie müssen den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
#     * Keine kommerzielle Nutzung.
#       Dieses Werk darf nicht für kommerzielle Zwecke verwendet werden.
#     * Weitergabe unter gleichen Bedingungen.
#       Wenn Sie den lizenzierten Inhalt bearbeiten oder in anderer Weise umgestalten,
#       verändern oder als Grundlage für einen anderen Inhalt verwenden,
#       dürfen Sie den neu entstandenen Inhalt nur unter Verwendung von Lizenzbedingungen
#       weitergeben, die mit denen dieses Lizenzvertrages identisch oder vergleichbar sind.
#
# Im Falle einer Verbreitung müssen Sie anderen die Lizenzbedingungen, unter welche dieses
# Werk fällt, mitteilen. Am Einfachsten ist es, einen Link auf diese Seite einzubinden.
#
# Jede der vorgenannten Bedingungen kann aufgehoben werden, sofern Sie die Einwilligung
# des Rechteinhabers dazu erhalten.
#
# Diese Lizenz lässt die Urheberpersönlichkeitsrechte unberührt.
#
# Weitere Details zur Lizenzbestimmung gibt es hier:
#   Kurzform: http://creativecommons.org/licenses/by-nc-sa/3.0/de/
#   Komplett: http://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode
#
###############################################################################
#
# 2009-10-10 0.0.1 rw created
# 2009-12-05 0.0.2 rw check, if no WP is defined
# 2010-01-27 0.0.3 rw UsedCapacity, Current
#                     switch of logging while sim is active
#
###############################################################################

$Version{'libmksim.pl'} = "0.0.3 - 2010-01-27";

my $Simulator = "OFF";

sub MkSim()
    {
    # switch off logging
    $LogQueue->enqueue( "OFF" );

    my $popup = $main->Toplevel();
    $popup->title("MikroKopter Simulator");

    # Catch delete window event and exit sim
    $popup->protocol( 'WM_DELETE_WINDOW' => sub
        {
        &CbSimStop();
        $popup->destroy();
        });


    my $menu_bar = $popup->Menu;
    $popup->optionAdd("*tearOff", "false");
    $popup->configure ('-menu' => $menu_bar);

    my $menu_action = $menu_bar->cascade('-label' => "Action");
    $menu_action->command('-label' => "3D Fix",
                          '-command' => [\&CbSim3DFix ],
                        );
    $menu_action->command('-label' => "Make MK Fly",
                          '-command' => [\&CbSimMkFly ],
                        );
    $menu_action->separator;                                   
    $menu_action->command('-label' => "Start Simulator",
                          '-command' => [\&CbSimStart ],
                         );
    $menu_action->command('-label' => "Stop Simulator",
                          '-command' => [\&CbSimStop ],
                        );

    my $frame = $popup->Frame() -> pack('-side' => 'top',
                                        '-expand' => 'y',
                                        '-anchor' => 's',
                                        '-padx' => 5,
                                        '-pady' => 5,
                                        );

    my $button = $popup->Frame() -> pack('-side' => 'bottom',
                                          '-expand' => 'y',
                                          '-anchor' => 's',
                                          '-padx' => 5,
                                          '-pady' => 5,
                                          );

     # Exit Button
     $button->Button('-text'    => 'Exit',
                     '-width' => 10,
                     '-command' => sub
                {
                &CbSimStop();
                $popup->destroy();
                }) -> pack( '-anchor' => 's',
                            '-padx' => 5,
                            '-pady' => 5,
                          );


    # Tabs erstellen
    my $book = $frame->NoteBook()->pack( -fill=>'both', -expand=>1 );

    #
    # Tab: GPS
    #
    my $GpsTab = $book->add( "GPS", -label=>"GPS" );

    # canvas grid position
    my $GpsRow = 0;
    my $GpsCol = 0;
    my $GpsRowMap = $GpsRow + 1;
    my $GpsColMap = $GpsCol;
    my $GpsRowAlt = $GpsRow + 2;
    my $GpsColAlt = $GpsCol;
    my $GpsRowStat = $GpsRow + 4;
    my $GpsColStat = $GpsCol;
    my $GpsRowSats = $GpsRow + 5;
    my $GpsColSats = $GpsCol;

    # Create and scale Photo
    my $ImgWidth = "$Cfg->{'map'}->{'SimImageSize'}" || 300;
    my $Factor = $MapSizeX / $ImgWidth;

    my $Img1 = $popup->Photo( 'SimFoto',
                              '-file' => "$Cfg->{'map'}->{'MapDir'}/$Map{'File'}",
                            );
    my $Img2 = $popup->Photo ('SimFoto-Resized');
    $Img2->copy ( $Img1,
                  '-shrink',
                  '-subsample' => $Factor, $Factor,
                 );

    my $ImgWidth  = $Img2->width;
    my $ImgHeight = $Img2->height;
    my $ImgScaleX = $MapSizeX / $ImgWidth;
    my $ImgScaleY = $MapSizeY / $ImgHeight;

    # display scaled Photo on canvas
    my $canvas = $GpsTab->Canvas( '-width'  => $ImgWidth,
                                  '-height' => $ImgHeight,
                                ) -> grid (-row    => $GpsRowMap,
                                           -column => $GpsColMap,
                                           -columnspan => 4,
                                          );
    $canvas->createImage( 0, 0,
                          '-tags'   => 'SimMap',
                          '-anchor' => 'nw',
                          '-image'  => $Img2,
                         );

    # Circle-Icon for MK, Target, Home
    my $Dia = 14;
    my $ImgSplit = $ImgWidth / 4;
    my $MkX     = $ImgSplit - $Dia/2;
    my $MkY     = $ImgHeight / 2 + $Dia/2;
    my $TargetX = $ImgSplit * 2  - $Dia/2;
    my $TargetY = $ImgHeight / 2 + $Dia/2;
    my $HomeX   = $ImgSplit * 3  - $Dia/2;
    my $HomeY   = $ImgHeight / 2 + $Dia/2;


    ($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'})       = &MapXY2Gps ($MkX * $ImgScaleX, $MkY * $ImgScaleY);
    ($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'}) = &MapXY2Gps ($TargetX * $ImgScaleX, $TargetY * $ImgScaleY);
    ($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'})     = &MapXY2Gps ($HomeX * $ImgScaleX, $HomeY * $ImgScaleY);

    $canvas->createOval ( $HomeX, $HomeY, $HomeX + $Dia, $HomeY + $Dia,
                         '-tags'    => "SimHome",
                         '-fill'    => $Cfg->{'mkcockpit'}->{'ColorHomeLine'},
                         '-outline' => "white",
                       );
    $canvas->createOval ( $TargetX, $TargetY, $TargetX + $Dia, $TargetY + $Dia,
                         '-tags'    => "SimTarget",
                         '-fill'    => $Cfg->{'mkcockpit'}->{'ColorTargetLine'},
                         '-outline' => "white",
                       );
    $canvas->createOval ( $MkX, $MkY, $MkX + $Dia, $MkY + $Dia,
                         '-tags'    => "SimMk",
                         '-fill'    => $Cfg->{'mkcockpit'}->{'ColorMkSatGood'},
                         '-outline' => "white",
                       );

    # GPS Alt MK
    $GpsTab->Label(-text => , $Translate{'CurPos_Alt'},
                  )->grid ('-row'    => $GpsRowAlt,
                           '-column' => $GpsColAlt,
                           '-sticky' => 'w',
                         );

    $MkOsd{'CurPos_Alt'} = 0;
    $scCurPos_Alt = $GpsTab->Scale(
                    '-orient'       => 'vertical',
                    '-from'         => 400,
                    '-to'           => 0,
                    '-tickinterval' => 50,
                    '-resolution'   => 1,
                    '-label'        => "",
                    '-font'         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                    '-length'       => 150,
                    '-width'        => 15,
                    '-variable'     => \$MkOsd{'CurPos_Alt'},
                   )->grid ('-row'    => $GpsRowAlt+1,
                            '-column' => $GpsColAlt,
                           );

    $MkOsd{'CurPos_Stat'} = 0;
    $cbCurPos_Stat = $GpsTab->Checkbutton(
                      -text => $Translate{'CurPos_Stat'},
                      -offvalue  => 0x00,
                      -onvalue   => 0x01,
                      -variable  => \$MkOsd{'CurPos_Stat'},
                     )->grid (-row    => $GpsRowStat,
                              -column => $GpsColStat,
                              -columnspan => 2,
                              -sticky => 'w',
                             );

    # Altimeter
    $GpsTab->Label(-text => , $Translate{'Altimeter'},
                  )->grid ('-row'    => $GpsRowAlt,
                           '-column' => $GpsColAlt +1,
                         );

    $MkOsd{'Altimeter'} = 0;
    $scAltimeter = $GpsTab->Scale(
                  -orient       => 'vertical',
                  -from         => 8000,
                  -to           => 0,
                  -tickinterval => 2000,
                  -resolution   => 1,
                  -label        => "",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 150,
                  -width        => 15,
                  -variable     => \$MkOsd{'Altimeter'},
                  )->grid (-row    => $GpsRowAlt +1,
                           -column => $GpsColAlt +1,
                          );

    # GPS Alt Target
    $GpsTab->Label(-text => , $Translate{'TargetPos_Alt'},
                  )->grid ('-row'    => $GpsRowAlt,
                           '-column' => $GpsColAlt +2,
                         );

    $MkOsd{'TargetPos_Alt'} = 0;
    $scTargetPos_Alt = $GpsTab->Scale(
                    '-orient'       => 'vertical',
                    '-from'         => 400,
                    '-to'           => 0,
                    '-tickinterval' => 50,
                    '-resolution'   => 1,
                    '-label'        => "",
                    '-font'         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                    '-length'       => 150,
                    '-width'        => 15,
                    '-variable'     => \$MkOsd{'TargetPos_Alt'},
                   )->grid ('-row'    => $GpsRowAlt +1,
                            '-column' => $GpsColAlt +2,
                           );

    $MkOsd{'TargetPos_Stat'} = 0;
    $cbTargetPos_Stat = $GpsTab->Checkbutton(-text => $Translate{'TargetPos_Stat'},
                      -offvalue  => 0x00,
                      -onvalue   => 0x01,
                      -variable  => \$MkOsd{'TargetPos_Stat'},
                     )->grid (-row    => $GpsRowStat,
                              -column => $GpsColStat +2,
                             );

    # GPS Alt Home
    $GpsTab->Label(-text => , $Translate{'HomePos_Alt'},
                  )->grid ('-row'    => $GpsRowAlt,
                           '-column' => $GpsColAlt +3,
                         );

    $MkOsd{'HomePos_Alt'} = 0;
    $scHomePos_Alt = $GpsTab->Scale(
                    '-orient'       => 'vertical',
                    '-from'         => 400,
                    '-to'           => 0,
                    '-tickinterval' => 50,
                    '-resolution'   => 1,
                    '-label'        => "",
                    '-font'         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                    '-length'       => 150,
                    '-width'        => 15,
                    '-variable'     => \$MkOsd{'HomePos_Alt'},
                   )->grid ('-row'    => $GpsRowAlt +1,
                            '-column' => $GpsColAlt +3,
                           );

    $MkOsd{'HomePos_Stat'} = 0;
    $cbHomePos_Stat = $GpsTab->Checkbutton(
                      -text => "Home Status",   # $Translate{'HomePos_Stat'},
                      -offvalue  => 0x00,
                      -onvalue   => 0x01,
                      -variable  => \$MkOsd{'HomePos_Stat'},
                     )->grid (-row    => $GpsRowStat,
                              -column => $GpsColStat +3,
                             );

    # Sats in Use
    $MkOsd{'SatsInUse'} = 0;
    $scSatsInUse = $GpsTab->Scale(
                   '-orient'       => 'horizontal',
                   '-from'         => 0,
                   '-to'           => 12,
                   '-resolution'   => 1,
                   '-tickinterval' => 1,
                   '-label'        => $Translate{'SatsInUse'},
                   '-font'         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   '-length'       => 300,
                   '-width'        => 15,
                   '-variable'     => \$MkOsd{'SatsInUse'},
                   )->grid (-row    => $GpsRowSats,
                            -column => $GpsColSats,
                            -columnspan => 4,
                           );

     # Balloon
     my $simballoon = $popup->Balloon();
     $simballoon->attach($canvas,
                         '-balloonposition' => 'mouse',
                         '-state' => 'balloon',
                         '-msg' => { 'SimMk'     => 'MikroKopter',
                                     'SimTarget' => 'Target',
                                     'SimHome'   => 'Home',
                                   },
                        );

    # Mouse button 1 for MK
    my $MkOldx = 0;
    my $MkOldy = 0;

    # Pick MK
    $canvas->bind('SimMk' => '<Button-1>' => sub
        {
        # prepare to move
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        $MkOldx = $x;
        $MkOldy = $y;
        });

    # Move Mk
    $canvas->bind('SimMk' => '<Button1-Motion>' => sub
        {
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        my $id      = $canvas->find('withtag', 'current');

        my $Dia2 = $Dia/2;
        if ( $x < $Dia2 ) { $x = $Dia2 };
        if ( $y < $Dia2 ) { $y = $Dia2 };
        if ( $x > $ImgWidth - $Dia2 ) { $x = $ImgWidth - $Dia2 };
        if ( $y > $ImgHeight - $Dia2) { $y = $ImgHeight - $Dia2 };

        $canvas->move($id => $x - $MkOldx, $y - $MkOldy);
        $MkOldx = $x;
        $MkOldy = $y;

        ($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}) = &MapXY2Gps ($x * $ImgScaleX, $y * $ImgScaleY);
        });

    # Mouse button 1 for Target
    my $TargetOldx = 0;
    my $TargetOldy = 0;

    # Pick Target
    $canvas->bind('SimTarget' => '<Button-1>' => sub
        {
        # prepare to move
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        $TargetOldx = $x;
        $TargetOldy = $y;
        });

    # Move Target
    $canvas->bind('SimTarget' => '<Button1-Motion>' => sub
        {
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        my $id      = $canvas->find('withtag', 'current');

        my $Dia2 = $Dia/2;
        if ( $x < $Dia2 ) { $x = $Dia2 };
        if ( $y < $Dia2 ) { $y = $Dia2 };
        if ( $x > $ImgWidth - $Dia2 ) { $x = $ImgWidth - $Dia2 };
        if ( $y > $ImgHeight - $Dia2) { $y = $ImgHeight - $Dia2 };

        $canvas->move($id => $x - $TargetOldx, $y - $TargetOldy);
        $TargetOldx = $x;
        $TargetOldy = $y;

        ($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'}) = &MapXY2Gps ($x * $ImgScaleX, $y * $ImgScaleY);
        });

    # Mouse button 1 for Home
    my $HomeOldx = 0;
    my $HomeOldy = 0;

    # Pick Home
    $canvas->bind('SimHome' => '<Button-1>' => sub
        {
        # prepare to move
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        $HomeOldx = $x;
        $HomeOldy = $y;
        });

    # Move Home
    $canvas->bind('SimHome' => '<Button1-Motion>' => sub
        {
        my ($x, $y) = ($Tk::event->x, $Tk::event->y);
        my $id      = $canvas->find('withtag', 'current');

        my $Dia2 = $Dia/2;
        if ( $x < $Dia2 ) { $x = $Dia2 };
        if ( $y < $Dia2 ) { $y = $Dia2 };
        if ( $x > $ImgWidth - $Dia2 ) { $x = $ImgWidth - $Dia2 };
        if ( $y > $ImgHeight - $Dia2) { $y = $ImgHeight - $Dia2 };

        $canvas->move($id => $x - $HomeOldx, $y - $HomeOldy);
        $HomeOldx = $x;
        $HomeOldy = $y;

        ($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}) = &MapXY2Gps ($x * $ImgScaleX, $y * $ImgScaleY);
        });


    #
    # Tab: Navigation
    #
    my $NavTab = $book->add( "Navigation", -label=>"Navigation", );

    my $Row = 0;
    my $Col = 0;

    # Waypoint Index
    $MkOsd{'WaypointIndex'} = 0;
    $scWaypointIndex = $NavTab->Scale(
                   -orient       => 'horizontal',
                   -from         => 0,
                   -to           => 20,
                   -tickinterval => 5,
                   -resolution   => 1,
                   -label        => $Translate{'WaypointIndex'},
                   -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   -length       => 300,
                   -width        => 15,
                   -variable     => \$MkOsd{'WaypointIndex'},
                   )->grid (-row    => $Row + 1,
                            -column => $Col,
                           );

    # Waypoint Number
    $MkOsd{'WaypointNumber'} = 0;
    $scWaypointNumber = $NavTab->Scale(
                   -orient       => 'horizontal',
                   -from         => 0,
                   -to           => 20,
                   -tickinterval => 5,
                   -resolution   => 1,
                   -label        => $Translate{'WaypointNumber'},
                   -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   -length       => 300,
                   -width        => 15,
                   -variable     => \$MkOsd{'WaypointNumber'},
                   )->grid (-row    => $Row + 2,
                            -column => $Col,
                           );

    # Operating Radius
    $MkOsd{'OperatingRadius'} = 250;
    $scOperatingRadius = $NavTab->Scale(
                   -orient       => 'horizontal',
                   -from         => 0,
                   -to           => 250,
                   -tickinterval => 50,
                   -resolution   => 1,
                   -label        => "$Translate{'OperatingRadius'} (m)",
                   -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   -length       => 300,
                   -width        => 15,
                   -variable     => \$MkOsd{'OperatingRadius'},
                   )->grid (-row    => $Row + 3,
                            -column => $Col,
                           );

    # TargetPosDev_Dist
    $MkOsd{'TargetPosDev_Dist'} = 250;
    $scTargetPosDev_Dist = $NavTab->Scale(
                   -orient       => 'horizontal',
                   -from         => 0,
                   -to           => 500,
                   -tickinterval => 100,
                   -resolution   => 1,
                   -label        => "$Translate{'TargetPosDev_Dist'} (dm)",
                   -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   -length       => 300,
                   -width        => 15,
                   -variable     => \$MkOsd{'TargetPosDev_Dist'},
                   )->grid (-row    => $Row + 4,
                            -column => $Col,
                           );


    #
    # Tab: MK
    #
    my $MkTab = $book->add( "MikroKopter1", -label=>"MikroKopter 1", );

    my $Row = 0;
    my $Col = 0;

    # Battery
    $MkOsd{'UBat'} = 12.6;
    $scUBat = $MkTab->Scale(
                   -orient       => 'horizontal',
                   -from         => 6.0,
                   -to           => 18.0,
                   -tickinterval => 2,
                   -resolution   => 0.1,
                   -label        => "$Translate{'UBat'} (V)",
                   -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                   -length       => 200,
                   -width        => 15,
                   -variable     => \$MkOsd{'UBat'},
                   )->grid (-row    => $Row,
                            -column => $Col,
                           );

    # RC Quality
    $MkOsd{'RC_Quality'} = 190;
    $scRC_Quality = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 200,
                  -tickinterval => 50,
                  -resolution   => 1,
                  -label        => $Translate{'RC_Quality'},
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 200,
                  -width        => 15,
                  -variable     => \$MkOsd{'RC_Quality'},
                  )->grid (-row    => $Row +1,
                           -column => $Col,
                          );
    # CompassHeading
    $MkOsd{'CompassHeading'} = 0;
    $scCompassHeading = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 360,
                  -tickinterval => 45,
                  -resolution   => 1,
                  -label        => $Translate{'CompassHeading'},
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 200,
                  -width        => 15,
                  -variable     => \$MkOsd{'CompassHeading'},
                  )->grid (-row    => $Row +2,
                           -rowspan => 4,
                           -column => $Col,
                          );

    # GPS Groundspeed
    $MkOsd{'GroundSpeed'} = 0;
    $scGroundSpeed = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 1000,
                  -tickinterval => 200,
                  -resolution   => 1,
                  -label        => "$Translate{'GroundSpeed'} (dm/s)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 200,
                  -width        => 15,
                  -variable     => \$MkOsd{'GroundSpeed'},
                  )->grid (-row    => $Row + 6,
                           -rowspan => 4,
                           -column => $Col,
                          );

    # GPS Speed North
    $MkNcDebug{'Analog_21'} = 0;
    $scAnalog_21 = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => -1000,
                  -to           => 1000,
                  -tickinterval => 400,
                  -resolution   => 1,
                  -label        => "$Translate{'Analog_21'} (dm/s)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 200,
                  -width        => 15,
                  -variable     => \$MkNcDebug{'Analog_21'},
                  )->grid (-row    => $Row + 10,
                           -rowspan => 4,
                           -column => $Col,
                          );

    # GPS Speed East
    $MkNcDebug{'Analog_22'} = 0;
    $scAnalog_22 = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => -1000,
                  -to           => 1000,
                  -tickinterval => 400,
                  -resolution   => 1,
                  -label        => "$Translate{'Analog_22'} (dm/s)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 200,
                  -width        => 15,
                  -variable     => \$MkNcDebug{'Analog_22'},
                  )->grid (-row    => $Row + 14,
                           -rowspan => 4,
                           -column => $Col,
                          );

    # Variometer
    $MkOsd{'Variometer'} = 0;
    $scVariometer = $MkTab->Scale(
                  -orient       => 'vertical',
                  -from         => 30,
                  -to           => -30,
                  -tickinterval => 10,
                  -resolution   => 1,
                  -label        => $Translate{'Variometer'},
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 100,
                  -width        => 15,
                  -variable     => \$MkOsd{'Variometer'},
                  )->grid (-row    => $Row,
                           -rowspan => 2,
                           -column => $Col + 1,
                          );

    my $DataLink = 1;
    $cbDataLink = $MkTab->Checkbutton(
                        -text => $Translate{'DataLink'},
                        -offvalue => 0x00,
                        -onvalue  => 0x01,
                        -variable  => \$DataLink,
                       )->grid (-row    => $Row + 2,
                                -column => $Col +1,
                                -sticky=>'w',
                                -ipadx => 10,
                               );

    $MkTab->Label(-text => "",                    # space
                 )->grid (-row    => $Row +3,
                          -column => $Col +1,
                          -sticky=>'w',
                          -ipadx => 10,
                         );

    $MkTab->Label(-text => "$Translate{'MKFlags'}: --------",
                 )->grid (-row    => $Row +4,
                          -column => $Col +1,
                          -sticky=>'w',
                          -ipadx => 10,
                         );
    my $MkMotorRun = 0;
    $cbMkMotorRun = $MkTab->Checkbutton(
                        -text => $Translate{'MkMotorRun'},
                        -offvalue => 0x00,
                        -onvalue  => 0x01,
                        -variable  => \$MkMotorRun,
                       )->grid (-row    => $Row + 5,
                                -column => $Col +1,
                                -sticky=>'w',
                                -ipadx => 10,
                               );
    my $MkFly = 0;
    $cbMkFly = $MkTab->Checkbutton(
                      -text => $Translate{'MkFly'},
                      -variable  => \$MkFly,
                      -offvalue => 0x00,
                      -onvalue  => 0x02,
                     )->grid (-row    => $Row + 6,
                              -column => $Col +1,
                                -sticky=>'w',
                                -ipadx => 10,
                             );
    my $MkCalibrate = 0;
    $cbMkCalibrate = $MkTab->Checkbutton(
                      -text => $Translate{'MkCalibrate'},
                      -variable  => \$MkCalibrate,
                      -offvalue => 0x00,
                      -onvalue  => 0x04,
                     )->grid (-row    => $Row + 7,
                              -column => $Col +1,
                               -sticky=>'w',
                               -ipadx => 10,
                             );
    my $MkStart = 0;
    $cbMkStart = $MkTab->Checkbutton(
                      -text => $Translate{'MkStart'},
                      -variable  => \$MkStart,
                      -offvalue => 0x00,
                      -onvalue  => 0x08,
                     )->grid (-row    => $Row + 8,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $MkEmergency = 0;
    $cbMkEmergency = $MkTab->Checkbutton(
                      -text => $Translate{'MkEmergency'},
                      -variable  => \$MkEmergency,
                      -offvalue => 0x00,
                      -onvalue  => 0x10,
                     )->grid (-row    => $Row + 9,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );

    $MkTab->Label(-text => "$Translate{'NCFlags'}: --------",
                 )->grid (-row    => $Row + 10,
                          -column => $Col + 1,
                           -sticky=>'w',
                           -ipadx => 10,
                         );
    my $NcFlagFree = 0;
    $cbNcFlagFree = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagFree'},
                      -offvalue => 0x00,
                      -onvalue  => 0x01,
                      -variable  => \$NcFlagFree,
                     )->grid (-row    => $Row + 11,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $NcFlagPH = 0;
    $cbNcFlagPH = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagPH'},
                      -offvalue => 0x00,
                      -onvalue  => 0x02,
                      -variable  => \$NcFlagPH,
                     )->grid (-row    => $Row + 12,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $NcFlagCH = 0;
    $cbNcFlagCH = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagCH'},
                      -offvalue => 0x00,
                      -onvalue  => 0x04,
                      -variable  => \$NcFlagCH,
                     )->grid (-row    => $Row + 13,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $NcFlagRangeLimit = 0;
    $cbNcFlagRangeLimit = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagRangeLimit'},
                      -offvalue => 0x00,
                      -onvalue  => 0x08,
                      -variable  => \$NcFlagRangeLimit,
                     )->grid (-row    => $Row + 14,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $NcFlagNoSerialLink = 0;
    $cbNcFlagNoSerialLink = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagNoSerialLink'},
                      -offvalue => 0x00,
                      -onvalue  => 0x10,
                      -variable  => \$NcFlagNoSerialLink,
                     )->grid (-row    => $Row + 15,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );
    my $NcFlagTargetReached = 0;
    $cbNcFlagTargetReached = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagTargetReached'},
                      -offvalue => 0x00,
                      -onvalue  => 0x20,
                      -variable  => \$NcFlagTargetReached,
                     )->grid (-row    => $Row + 16,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );

    my $NcFlagManualControl = 0;
    $cbNcFlagManualControl = $MkTab->Checkbutton(
                      -text => $Translate{'NcFlagManualControl'},
                      -offvalue => 0x00,
                      -onvalue  => 0x40,
                      -variable  => \$NcFlagManualControl
                     )->grid (-row    => $Row + 17,
                              -column => $Col + 1,
                              -sticky=>'w',
                              -ipadx => 10,
                             );

    #
    # Tab: MK 2
    #
    my $Mk2Tab = $book->add( "MikroKopter2", -label=>"MikroKopter 2", );

    my $Row = 0;
    my $Col = 0;

    # Current
    $MkOsd{'Current'} = 0;
    $scCurrent = $Mk2Tab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 50,
                  -tickinterval => 10,
                  -resolution   => 1,
                  -label        => "$Translate{'Current'} (A)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 300,
                  -width        => 15,
                  -variable     => \$MkOsd{'Current'},
                  )->grid (-row    => $Row,
                           -column => $Col,
                          );

    # Used Capacity
    $MkOsd{'UsedCapacity'} = 0;
    $scUsedCapacity = $Mk2Tab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 5000,
                  -tickinterval => 1000,
                  -resolution   => 50,
                  -label        => "$Translate{'UsedCapacity'} (mAh)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 300,
                  -width        => 15,
                  -variable     => \$MkOsd{'UsedCapacity'},
                  )->grid (-row    => $Row + 1,
                           -column => $Col,
                          );

    #
    # Tab: Simulator
    #
    my $MkTab = $book->add( "Simulator", -label=>"Simulator", );

    my $Row = 0;
    my $Col = 0;

    # Speed
    my $SimSpeed = 20;  # km/h
    $scSimSpeed = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 50,
                  -tickinterval => 5,
                  -resolution   => 1,
                  -label        => "Speed (km/h)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 300,
                  -width        => 15,
                  -variable     => \$SimSpeed,
                  )->grid (-row    => $Row,
                           -column => $Col,
                          );

    # Acceleration
    my $SimAcc = 10;  # m/s**2
    $scSimAcc = $MkTab->Scale(
                  -orient       => 'horizontal',
                  -from         => 0,
                  -to           => 30,
                  -tickinterval => 5,
                  -resolution   => 0.5,
                  -label        => "Acceleration (m/s**2)",
                  -font         => '-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1',
                  -length       => 300,
                  -width        => 15,
                  -variable     => \$SimAcc,
                  )->grid (-row    => $Row +1,
                           -column => $Col,
                          );


    my $Speed  = 0;  # Current Mk Speed
    my $SpeedN = 0;  # Speed North
    my $SpeedE = 0;  # Speed East


    #
    # Timer
    #
    my $SimTimebase = 100;    # Simulator Timebase in ms
    $popup->repeat ($SimTimebase, sub
        {
        lock (%MkOsd);              # until end of block

        $MkOsd{'MKFlags'} = $MkMotorRun | $MkFly | $MkCalibrate | $MkStart | $MkEmergency;
        $MkOsd{'NCFlags'} = $NcFlagFree | $NcFlagPH | $NcFlagCH | $NcFlagRangeLimit |
                            $NcFlagNoSerialLink | $NcFlagTargetReached | $NcFlagManualControl;

        # Calibration sequence
        if ( $CalibCount > 0 )
            {
            $CalibCount ++;
            }
        if ( $CalibCount > 2 * 1000 / $SimTimebase )  # 2s
            {
            $cbMkCalibrate-> deselect();
            $cbMkStart->deselect();

            $cbMkMotorRun->select();
            $cbMkFly->select();

            $CalibCount = 0;
            }

        #
        # Simulator
        #
        if ( $Simulator =~ /ON/i and
             $MkSim{'Target_Lat'} ne ""  and $MkSim{'Target_Lon'} ne "" )
            {
            # Set Target-Pos
            $MkOsd{'TargetPos_Lat'} = $MkSim{'Target_Lat'};
            $MkOsd{'TargetPos_Lon'} = $MkSim{'Target_Lon'};
            $MkOsd{'TargetPos_Alt'} = $MkSim{'Target_Alt'};
            $MkOsd{'TargetPos_Stat'} = 1;

            my ($HomeDist, $HomeBearing) = &MapGpsTo($MkOsd{'HomePos_Lat'},   $MkOsd{'HomePos_Lon'},
                                                     $MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );
            if ( $HomeDist > $MkOsd{'OperatingRadius'} )
                {
                # Target entsprechend Operation Radius neu berechnen
                $HomeDist = $MkOsd{'OperatingRadius'};
                ($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'}) = &MapGpsAt($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'},
                                                                               $HomeDist, $HomeBearing);
                $cbNcFlagRangeLimit->select;
                }
            else
                {
                $cbNcFlagRangeLimit->deselect;
                }

            #
            # Mk physics
            # Move MK to Target with constant acceleration
            #
            my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
                                             $MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );

            $MkOsd{'TargetPosDev_Dist'} = $Dist * 10;  # in dm
            $MkOsd{'TargetPosDev_Bearing'} = $Bearing;

            my $AccN = $SimAcc * cos (deg2rad $Bearing);     # Acceleration North
            my $AccE = $SimAcc * sin (deg2rad $Bearing);     # Acceleration East
            my $t = $SimTimebase / 1000;

            $SpeedN = $SpeedN + $AccN * $t;  # Speed North
            $SpeedE = $SpeedE + $AccE * $t;  # Speed East
            $Speed  = sqrt ($SpeedN * $SpeedN + $SpeedE * $SpeedE);

            if ( $Speed >= $SimSpeed/3.6 )
                {
                # Limit maximum Speed
                my $SpeedBearing = rad2deg (atan2 ($SpeedE, $SpeedN));

                $SpeedN = $SimSpeed/3.6 * cos (deg2rad $SpeedBearing);
                $SpeedE = $SimSpeed/3.6 * sin (deg2rad $SpeedBearing);
                $Speed = $SimSpeed/3.6;
                }

            my $BreakDist = 5;
            if ( $Dist <= $BreakDist )
                {
                $Speed = $SimSpeed/3.6 - $SimSpeed/3.6 * (1 - $Dist/$BreakDist);
                my $SpeedBearing = rad2deg (atan2 ($SpeedE, $SpeedN));
                $SpeedN = $Speed * cos (deg2rad $SpeedBearing);
                $SpeedE = $Speed * sin (deg2rad $SpeedBearing);
                $AccN = 0;
                $AccE = 0;
                }
               
            # Distance to go in this loop
            my $GoDistN = $SpeedN * $t + 0.5 * $AccN * $t * $t;
            my $GoDistE = $SpeedE * $t + 0.5 * $AccE * $t * $t;
            my $GoDist = sqrt ( $GoDistN * $GoDistN + $GoDistE * $GoDistE);
            my $GoBearing = rad2deg (atan2 ($GoDistE, $GoDistN));

            if ($GoDist > 0 )
                {
                ($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}) = &MapGpsAt($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
                                                                         $GoDist, $GoBearing);            
                }

            if ( $Dist < $MkSim{'Target_ToleranceRadius'} )
                {
                $cbNcFlagTargetReached -> select();
                }
            else
                {
                $cbNcFlagTargetReached -> deselect();
                }

            # GPS Groundspeed, North, East
            $MkOsd{'GroundSpeed'}  = $Speed * 100;  # dm/s
            $MkNcDebug{'Analog_21'} = $SpeedN * 100;
            $MkNcDebug{'Analog_22'} = $SpeedE * 100;

            # Heading
            if ( $MkSim{'Target_Heading'} != 0 )
                {
                $scCompassHeading -> set($MkSim{'Target_Heading'});
                }

            }

        # update display
        $scAnalog_21 -> set($MkNcDebug{'Analog_21'});
        $scAnalog_22 -> set($MkNcDebug{'Analog_22'});
        $scTargetPos_Alt -> set($MkOsd{'TargetPos_Alt'});
        $scTargetPosDev_Dist -> set ($MkOsd{'TargetPosDev_Dist'});
        $scGroundSpeed -> set($MkOsd{'GroundSpeed'});
        $scWaypointNumber -> set ($MkOsd{'WaypointNumber'});
        $scWaypointIndex -> set ($MkOsd{'WaypointIndex'});

        # move MK symbol on canvas
        my ($x, $y) = &MapGps2XY ($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'});
        $x = $x / $ImgScaleX;
        $y = $y / $ImgScaleY;
        $canvas->coords ('SimMk', $x, $y, $x + $Dia, $y + $Dia);

        # move Home symbol on canvas
        my ($x, $y) = &MapGps2XY ($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'});
        $x = $x / $ImgScaleX;
        $y = $y / $ImgScaleY;
        $canvas->coords ('SimHome', $x, $y, $x + $Dia, $y + $Dia);

        # move Target symbol on canvas
        my ($x, $y) = &MapGps2XY ($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
        $x = $x / $ImgScaleX;
        $y = $y / $ImgScaleY;
        $canvas->coords ('SimTarget', $x, $y, $x + $Dia, $y + $Dia);

        if ( $DataLink)
            {
            # Timestamp, wann der Datensatz geschrieben wurde
            $MkOsd{'_Timestamp'} = time;
            }

        });
    }

# Make MK Fly
sub CbSimMkFly
    {
    $scWaypointIndex->set(5);
    $scWaypointNumber->set(10);
    $cbNcFlagCH->select();

    # start calibration sequence
    $cbMkCalibrate-> select();
    $cbMkStart->select();
    $CalibCount = 1;
    }


# 3D Fix
sub CbSim3DFix
    {
    my $Alt = 50;
    $scCurPos_Alt->set($Alt);
    $scAltimeter->set($Alt * $Cfg->{'map'}->{'AltFactor'});

    $scSatsInUse->set(10);
    $cbCurPos_Stat->select();
    $cbTargetPos_Stat->select();
    $cbHomePos_Stat->select();

    # Set Home-Pos to Cur-Pos
    $MkOsd{'HomePos_Lat'} = $MkOsd{'CurPos_Lat'};
    $MkOsd{'HomePos_Lon'} = $MkOsd{'CurPos_Lon'};

    $scGroundSpeed->set(400);
    $scAnalog_21->set(10);
    $scAnalog_22->set(10);
    }

# Switch Simulator ON
sub CbSimStart
    {
    $Simulator = "ON";

    # Only one Target from Player
    $MkOsd{'WaypointNumber'} = 1;
    $MkOsd{'WaypointIndex'} = 0;
    }


# Switch Simulator OFF
sub CbSimStop
    {
    $Simulator = "OFF";

    # switch on logging
    $MkOsd{'_Timestamp'} = 0;
    $LogQueue->enqueue( "LOG" );
    }

       
1;

__END__