// Sim97v2: Video-Fahrsimulation Motiv-Projekt 1997
//
// Beginn der Arbeit an diesem exe-File: 4. September 1997
//
// März 2000: Diese abgewandelte Version für von M2-Application erstellte
// Bänder (analoger Output digital aufgenommener und bearbeiteter Videos)
//
// Basisadressen der Zusatzkarten (in Klammern dezimal)
//
// 0x218 (536) bis 0x220 (544) - 8 I/O-Adressen, Meßkarte DT2811
// 0x240 (576) bis 0x260 (608) - 32 I/O-Adressen, Timecodelesekarte PCL3

#include "sim97v2.h"   // This also includes windows.h
#include "brs525e2.h"  // Prototypes for dynalink brs525e.dll
#include "strip.h"     // Prototypes for dynalink strip.dll
#include "hartwich.h"  // Prototypes for dynalink hartwich.dll
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <conio.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include "ctl3d.h"     // Prototypes for dynalink ctl3dv2.dll (from Microsoft)
#include "anamtr.h"    // Analogmeter von Marcellus Buchheit
#include "mirror.h"    // Spiegeloptikansteuerung Dynalink

// Material zur Messung des Zeitverhaltens, die gemessene Iterationsdauer in Millisekunden wird dann in
// Kanal 9 ans Datenauswertungsprogramm gegeben.
#ifdef MEASURE_SPARETIME
FILE* TIMEDATLOGFILE;
DWORD MESPstartTime;
DWORD MESPendTime;
DWORD iterationTakesMSECS;
#endif
// Ende Material zur Messung des Zeitverhaltens

// A global work font
static HFONT globalWorkFont;

// Commands sent to the program "Monitor", built later
static BYTE MonitorCMD_StartRecording[SZTRANSMITQUEUE];
static BYTE MonitorCMD_EndRecording[SZTRANSMITQUEUE];

// Constants for pcl3
static const UINT registermixed=0x0001;                   // Register value for 'InstallReader'
static const UINT the_base=0x240;                         // Base address of pcl3 board
static const UINT do_sync=0x40;                           // Synchron mode selection
static const UINT do_async=0x80;                          // Asynchron mode selection

// Constants for dt2811
// Masks
static const BYTE edmask=0xc0;                            // 11000000 Error or done mask
static const BYTE errormask=0x40;                         // 01000000
static const BYTE done_and_error=0xc0;                    // 11000000
static const BYTE done=0x80;                              // 10000000

// Destination table
static const UINT base_address=0x218;                     // Base address of DT2811 board
static const UINT d_adcsr=0x218;                          // A/D Control/Status register
static const UINT d_adgcr=0x219;                          // A/D Gain/Channel (+1)
static const UINT d_addat_dadat0l=0x21a;                  // A/D and DAC0 Data Low Byte (+2)
static const UINT d_addat_dadat0h=0x21b;                  // A/D and DAC0 Data High Byte (+3)
static const UINT d_dadat1l=0x21c;                        // DAC1 Low Byte (+4)
static const UINT d_dadat1h=0x21d;                        // DAC1 High Byte (+5)
static const UINT d_diop1_diop2=0x21e;                    // DIO Port 0, DIO Port 1 (+6)
static const UINT d_tmrctr=0x21f;                         // Timer/Counter (+7)

// Commands table
static const int ADGCR_CH0=0x00;                          // 00000000 Set gain 1, set channel 0 (Lenk)
static const int ADGCR_CH1=0x01;                          // 00000001 Set gain 1, set channel 1 (Gas)
static const int ADGCR_CH2=0x02;                          // 00000010 Set gain 1, set channel 2 (Brems)
static const int ADCSR_MODE1=0x11;                        // 00010001 Mode 1 & clear ADERR
static const int ADCSR_DISABLE=0x00;                      // 00000000 Disable clock (to change channel)
// Experimenting with 10 Hz at the time
static const int TMRCTR_30HZ=0x3c;                        // This is 10 Hertz !!!!
//const int TMRCTR_30HZ=0x1c;                      // Description in Data Translation manual is wrong !
static const int TMRCTR_MAX=0x13;                         // This is 300 Hz, higher frequency does not work !

// Note: You definitely have to write 1Ch and NOT 14h to get 30 Hertz.
// Data Translation Manual must be wrong !

// Timecode globals
typedef enum { asynchron,synchron,permanent } reqmode;

struct reader {
   int baseaddress;                                // Base address of pcl3 board
   int addressregister;                            // Addresse of adress register
   int requestregister;                            // Addresse of request register
   int highaddress;                                // High-Nibble of address offset
   reqmode mode;                                   // Operation mode
} reader;

// Data Translation globals

typedef struct TWOBYTES_tag {
   BYTE low;
   BYTE high;
} TWOBYTES;

// Other globals

typedef enum {
  NOTH, // Pepare nothing
  LENK, // Prepare chart Lenkung
  GASP, // Prepare chart Gaspedal
  BREM, // Prepare chart Bremspedal
  RECO, // Prepare chart Recorderbefehle
  LGBC, // Prepare chart Lenkung-Gas-Bremse
  VELO, // Prepare chart System Geschwindigkeit
  HBTC, // Prepare chart Hupe-Blinker-Tempomat
  SYSL, // Prepare chart System Lenkung
  ALLC  // Prepare chart Allcharts
} CHARTSELECTOR;

static CHARTSELECTOR ChartRequired;

static char szStripClass[] = "stripchartwindow";
static char invisibleClass[]= "hiddenaway";               // Strange name, I have to admit
static char repClass[]= "Replay";

// Nonadjustable parameters the speed system

static double NormedSpeed;                                // "velocity" in naturalistic units
static int lenkw_in;
static int gaspd_in;
static int brems_in;
static int digital_in;

// Global structure containing all system goals

static struct glo_adj {
  BYTE starttc_st;
  BYTE starttc_mi;
  BYTE starttc_se;
  BYTE starttc_fr;
  BYTE endtc_st;
  BYTE endtc_mi;
  BYTE endtc_se;
  BYTE endtc_fr;
  char expname[9];                                 // 8 chars name of trial

  BYTE run_tacho:1;                                // Drive tachometer (D/A) or not
  BYTE run_cursor:1;                               // Draw cross for video projection

  BYTE run_mirror:1;                               // Use steering simulation with mirror
  BYTE do_sendserial:1;                            // Send data via COM3 to another computer
  BYTE crosspainting:1;                            // Painting (hide window frame) RESERVED !
  BYTE fillbits:3;                                 // Fill up the bitfield to byte boundary

  BOOL isCanceled;                                 // Allow for cancel in Dialog
} glo_adj;

// Globale Kontrollstruktur für die Kommunikation mit dem Datenauswerter

static struct Communication_tag {
  char ComName[5];                   // Serielle Schnittstelle ("COM1","COM2",usw.)
  int TheComID;                      // Rückgabewert von OpenComm
  DCB TheDCB;                        // Device Control Block
  WORD FAR *ComEVent;                // Eventmaskenzeiger (zum Polling)
  BOOL SetUpOK;                      // Serielle Schnittstelle geöffnet
} Com;

// Prototypes for Alpermann & Velte PCL-3 timecode reader functions

void installreader(void);
void requesttc(void);

// Prototypes for Data Translation DT 2811 I/O board

void dt_initialize(void);
void dt_set_ad_mode(void);
void dt_set_clock_freq_30Hz(void);
void dt_set_clock_freq_max(void);
int dt_get_channel_0(void);
int dt_get_channel_1(void);
int dt_get_channel_2(void);
void dt_send_channel_0(int value);
void dt_send_channel_1(int value);
void dt_disable_clock(void);
char dt_get_digital_in(void);
void delay(long dlt);

// Other functions prototypes

BOOL suche_bandstelle(char *codestring,HWND dialogsHandle,int textID);
void fillhighnibble(BYTE *bcd_targetbyte,char sourceNumber);
void filllownibble(BYTE *bcd_targeTbyte,char sourceNumber);
void init_glo_adj(void);
void FAR PASCAL DumpLogInfo(void);
void tape_to_start(void);
void AddExt(LPSTR Name,LPSTR Ext);
void win3dFrame(HWND hwnd,HDC hdc);
void DisableOtherMenues(void);
void ReEnableOtherMenues(void);
BOOL SecondTcQuadBigger(BYTE h1,BYTE m1,BYTE s1,BYTE f1,BYTE h2,BYTE m2,BYTE s2,BYTE f2);


// Timecode procedures and functions

// Install mixed register synchron

void installreader(void)
{
  reader.baseaddress=the_base;               // Base address of TC reader board (switched to 0x240)
  reader.requestregister=the_base+0x0e;
  reader.addressregister=the_base+0x10;      // Important for choosing one of the registers
  reader.highaddress=(char)pred(registermixed)+4*pred(registermixed);  // Hope you grasped it (0)
  reader.mode=synchron;
}

// Aynchron-Request

void requesttc(void)
{
  // Each read or write operation in PCL-3 starts with writing to baseadress+0x10
  // (here called "addressregister"), data is the address of the register you need to
  // operate on (I call this choosing one of the 16  registers):
  outp(reader.addressregister,reader.highaddress);
  // I have chosen the register No. zero, this is the mixed TC register. Now I write to byte 0xE
  // (You can call the 16 bytes of a register "registers", as Alperman & Velte often does). This
  // is called "requestregister" here. Data is 0x80 alias "do_async"; effect: PCL-3 gets you
  // the last read TC continuously, it does not wait until a new TC comes from tape (If you want
  // to await new TCs, use data 0x40):
  outp(reader.requestregister,do_async);
}

// Fill timecode-structure

int FAR PASCAL GetTC(TIMEANDUSER *tu) // Tip: The Alperman & Velte examples are very instructive
{
  int ready;

  // You can omit the following line if you operate on the same register again:
  outp(reader.addressregister,reader.highaddress); // See PCL-3 protocol

  // Is request byte (0xE) is 0x00, if it is, you can read the value
  ready=(inp(reader.requestregister) == 0);

  if (ready)
  {
    tu->bcdFr=(BYTE)inp(reader.baseaddress+0x00);
    tu->bcdSe=(BYTE)inp(reader.baseaddress+0x01);
    tu->bcdMi=(BYTE)inp(reader.baseaddress+0x02);
    tu->bcdSt=(BYTE)inp(reader.baseaddress+0x03);
    tu->us1_2=(BYTE)inp(reader.baseaddress+0x08);
    tu->us3_4=(BYTE)inp(reader.baseaddress+0x09);
    tu->us5_6=(BYTE)inp(reader.baseaddress+0x0a);
    tu->us7_8=(BYTE)inp(reader.baseaddress+0x0b);
  }

  return ready;
}

// Procedures and functions driving the DT 2811

// Initialize board

void dt_initialize(void)
{
  int lowbyte;
  int highbyte;

  outp(d_adcsr,0x0); // 1.1. Initialize board by writing 0 to A/D Control/Status Register

  delay(400000);     // 1.2. Manual says: "Wait for at least 100 microseconds"

  lowbyte=inp(d_addat_dadat0l);  // 1.3.Read the low byte
  highbyte=inp(d_addat_dadat0h); // and the high byte of the A/D and D/A Data Register
}

// Load the A/D Control/Status Register with the selected parameters for the A/D conversions

void dt_set_ad_mode(void)
{
  outp(d_adcsr,ADCSR_MODE1); // In this mode you always get the current value in the register
}

// Load the Timer/Counter Register with code for the selected output frequency

void dt_set_clock_freq_30Hz(void)
{
  outp(d_tmrctr,TMRCTR_30HZ);
}

void dt_set_clock_freq_max(void)
{
  outp(d_tmrctr,TMRCTR_MAX); // Maximum freq is partly determined by your software, see manual
}

// Get analog inputs ----------------------------------------------------------------------
// 1. Initiate conversion by loading the A/D Gain/Channel Register with selected gain and
//    channel number. A/D conversion starts with the first pulse from the internal clock.
// 2. Poll the A/D Done bit (ADCSR bit 7) until set.
// 3. Read ADCSR and determine if ERR bit is set, do diagnostics.
// 4. Read the converted data from the ADDAT/DADAT0; first the low byte then the high byte.
//    Reading the high byte of data clears the A/D Done bit. Goto 1.

// Get channel 0

int dt_get_channel_0(void)
{
   BYTE status;
   union dta {
      TWOBYTES hl;
      int data;
   } dta;

   outp(d_adgcr,ADGCR_CH0); // 1.

   while(((status=(BYTE)inp(d_adcsr))&edmask) != done) // 2. 3.
   {
      if ((status == errormask) || (status == done_and_error))
      {
         MessageBox(NULL,"GetChannel0 meldet Fehler",NULL,MB_ICONEXCLAMATION);
      }
   }

   dta.hl.low=(char)inp(d_addat_dadat0l);  // 4. Read lowbyte
   dta.hl.high=(char)inp(d_addat_dadat0h); // 4. Read highbyte
   return dta.data;
}

// Get channel 1

int dt_get_channel_1(void)
{
   BYTE status;
   union dta {
      TWOBYTES hl;
      int data;
   } dta;

   outp(d_adgcr,ADGCR_CH1); // 1.

   while(((status=(BYTE)inp(d_adcsr))&edmask) != done) // 2. 3.
   {
      if ((status == errormask) || (status == done_and_error))
      {
         MessageBox(NULL,"GetChannel1 meldet Fehler",NULL,MB_ICONEXCLAMATION);
      }
   }

   dta.hl.low=(char)inp(d_addat_dadat0l);  // 4. Read lowbyte
   dta.hl.high=(char)inp(d_addat_dadat0h); // 4. Read highbyte

   return dta.data;
}

// Get channel 2

int dt_get_channel_2(void)
{
   BYTE status;
   union dta {
      TWOBYTES hl;
      int data;
   } dta;

   outp(d_adgcr,ADGCR_CH2); // 1.

   while(((status=(BYTE)inp(d_adcsr))&edmask) != done) // 2. 3.
   {
      if ((status == errormask) || (status == done_and_error))
      {
         MessageBox(NULL,"GetChannel2 meldet Fehler",NULL,MB_ICONEXCLAMATION);
      }
   }

   dta.hl.low=(char)inp(d_addat_dadat0l);  // 4. Read lowbyte
   dta.hl.high=(char)inp(d_addat_dadat0h); // 4. Read highbyte
   return dta.data;
}

// D/A conversion, uses the first of the two D/A converters on DT 2811

void dt_send_channel_0(int value)
{
   outp(d_addat_dadat0l,value);    // Write least significant byte
   outp(d_addat_dadat0h,value>>8); // Writes most significant byte
}

// D/A conversion, uses the second of the two D/A cinverters on DT 2811

void dt_send_channel_1(int value)
{
   outp(d_dadat1l,value);    // Write least significant byte
   outp(d_dadat1h,value>>8); // Writes most significant byte
}

// Disable dt 2811´s clock -------------------------------------------------
// To change the input channel:
// A. Disable the internal clock outputs by writing to the ADCSR with 0
//    for the value of the mode field. This disables all hardware triggers.
//    Do this by:  dt_disable_clock();
// B. Continue: dt_set_clock_frequency();
//              dt_set_ad_mode();
//              dt_get_channel_N();
//              ...

void dt_disable_clock(void)
{
  outp(d_adcsr,ADCSR_DISABLE);
}

// Digital input function

char dt_get_digital_in(void)
{
  return ((char)inp(d_diop1_diop2));
}

// Delay routine

#pragma loop_opt(off) // Disable compiler´s loop optimization
void delay(long dlt)  // Delay dlt units dlt 400000 is approximately 120 msec
{
  long i;

  for(i=0;i<dlt;i++)
  {
  };
}
#pragma loop_opt(on)  // Enable compiler´s loop optimization

// Initialize system settings

void init_glo_adj(void)
{
  glo_adj.starttc_st=0x00;                         // Hours BCD format
  glo_adj.starttc_mi=0x00;                         // Minutes BCD format
  glo_adj.starttc_se=0x00;                         // Seconds BCD format
  glo_adj.starttc_fr=0x00;                         // Frames BCD format
  glo_adj.endtc_st=0x00;                           // Hours BCD format
  glo_adj.endtc_mi=0x00;                           // Minutes BCD format
  glo_adj.endtc_se=0x00;                           // Seconds BCD format
  glo_adj.endtc_fr=0x00;                           // Frames BCD format
  strcpy(glo_adj.expname,"TESTRUN");
  glo_adj.run_tacho=1;                             // Drive tachometer (D/A) or not
  glo_adj.run_cursor=0;                            // Draw cross for video projection
  glo_adj.run_mirror=1;                            // Use steering simulation with mirror
  glo_adj.do_sendserial=1;                         // Send data via COM3 to another computer
  //glo_adj.fillbits;                              // Fill up the bitfield to byte boundary
  glo_adj.crosspainting=0;                         // Screen blackness off RESERVED !
}

void FAR PASCAL DumpLogInfo(void)
{
  FILE *log;
  char message[64];
  char sayOn[5];
  char sayOff[5];

  strcpy(sayOn,"ein\n");    // Make it broad enough to overwrite the "Nein"
  strcpy(sayOff,"aus\n");

  log=fopen("sim97.log","a");

  if (log != NULL)
  {
    fputs(" - - - - - - - - - - Neuer Versuchslauf - - - - - - - - - - \n",log);

    sprintf(message,
            "Beginn bei Timecode .. %02x:%02x:%02x:%02x\n",
            glo_adj.starttc_st,
            glo_adj.starttc_mi,
            glo_adj.starttc_se,
            glo_adj.starttc_fr);
    fputs(message,log);

    sprintf(message,
            "Ende bei Timecode .... %02x:%02x:%02x:%02x\n",
            glo_adj.endtc_st,
            glo_adj.endtc_mi,
            glo_adj.endtc_se,
            glo_adj.endtc_fr);
    fputs(message,log);

    sprintf(message,"Meßdatei heißt %s.dat\n",glo_adj.expname);
    fputs(message,log);

    sprintf(message,"Tachoansteuerung ist ");
    if (glo_adj.run_tacho) strcat(message,sayOn);
    else strcat(message,sayOff);
    fputs(message,log);

    sprintf(message,"Fadenkreuzaufgabe ist ");
    if (glo_adj.run_cursor) strcat(message,sayOn);
    else strcat(message,sayOff);
    fputs(message,log);

    sprintf(message,"Spiegelnutzung ist ");
    if (glo_adj.run_mirror) strcat(message,sayOn);
    else strcat(message,sayOff);
    fputs(message,log);
  }

  fclose(log);
}


int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)
{
  MSG msg;           // MSG structure to store messages        .
  int nRc;           // return value from Register Classes          .

  strcpy(szAppName,"Sim97v2");
  hInst=hInstance;

  if(!hPrevInstance)
  {
    // register window classes if first instance of application         .
    if ((nRc=nCwRegisterClasses()) == -1)
    {
      // registering one of the windows failed                         .
      LoadString(hInst,IDS_ERR_REGISTER_CLASS,szString,sizeof(szString));
      MessageBox(NULL,szString,NULL,MB_ICONEXCLAMATION);
      return nRc;
    }
    // This is for the chicago ("win95")style look mand feel
    Ctl3dRegister(hInst);
    Ctl3dAutoSubclass(hInst);
    // Registrierung der Analoganzeigeinstrumente wird beim Startup von der DLL selbst vorgenommen
  }

  init_glo_adj();  // Initialize system settings, especially those untouched by GetSim97ProfileData

  // create application's Main window
  hWndMain = CreateWindow(
                szAppName,                          // Window class name
                "Sim97v2",                          // Window's title
                WS_CAPTION      |                   // Title and Min/Max
                WS_SYSMENU      |                   // Add system menu box
                WS_MINIMIZEBOX  |                   // Add minimize box
                WS_MAXIMIZEBOX  |                   // Add maximize box
                WS_THICKFRAME   |                   // thick sizeable frame
                WS_CLIPCHILDREN |                   // don't draw in child windows areas
                WS_OVERLAPPED,                      // Exclude children when painting
                CW_USEDEFAULT,                      // Use default X
                0,                                  // Use default Y
                CW_USEDEFAULT,                      // Use default width
                0,                                  // Use default height
                NULL,                               // Parent window's handle
                NULL,                               // Default to Class Menu
                hInst,                              // Instance of window
                NULL);                              // Create struct for WM_CREATE


  if(hWndMain == NULL)
  {
    LoadString(hInst,IDS_ERR_CREATE_WINDOW,szString,sizeof(szString));
    MessageBox(NULL,szString,NULL,MB_ICONEXCLAMATION);
    return IDS_ERR_CREATE_WINDOW;
  }

  globalWorkFont = CreateFont(14,0,0,0,100,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
           CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"Modern");

  // Sendekommandotabelle für Kommunikation mit Programm "Monitor" aufbauen
  memset(MonitorCMD_StartRecording,'#',sizeof(MonitorCMD_StartRecording));
  // Ab 21. Zeichen wird der Name eingefügt, Kommandomatchprozedur von Monitor
  // schaut sich nur die ersten 20 Zeichen an, um ein Kommando zu erkennen, der Rest
  // kann Informationen tragen, wie im Fall von MonitorCMD_StartRecording
  lstrcpy(MonitorCMD_StartRecording,"StartAufzeichnung###NAME");

  memset(MonitorCMD_EndRecording,'#',sizeof(MonitorCMD_EndRecording));
  lstrcpy(MonitorCMD_EndRecording,"EndAufzeichnung#####");

  GetSim97ProfileData();

  ShowWindow(hWndMain,SW_SHOWMAXIMIZED);            // display main window

  while(GetMessage(&msg,NULL,0,0))                  // Until WM_QUIT message
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  WriteSim97ProfileData();

  // Do clean up before exiting from the application
  CwUnRegisterClasses();
  Ctl3dUnregister(hInst);
  DeleteObject(globalWorkFont);

  return msg.wParam;
} // End of WinMain


// Main Window Procedure

LONG FAR PASCAL WndProc(HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
  HMENU hMenu=0;                                   // Handle for the menu
  HBITMAP hBitmap=0;                               // Handle for bitmaps
  HDC hDC;                                         // Handle for the display device
  PAINTSTRUCT ps;                                  // Holds PAINT information
  int nRc=0;                                       // Return code
  RECT rectClient;                                 // Rectangle for client area dimensions
  static HWND hwndStrip;                           // Handle for strip chart child window
  static HWND hwndExperiment;                      // Handle to main work-procedure
  static HWND hwndReplay;                          // Handle to replay WndProc
  static char titlestring[150];                    // Self explanatory
  static BOOL isStripOn;                           // Is a stripchart at work?

  switch (Message)
  {
    case WM_CREATE:
         isStripOn=FALSE;                          // Nicht beim Hardwaretest
         // Immer posten, damit erfolgt das Eintragen des Dateinamens in MonitorCMD_StartRecording
         PostMessage(hWnd,WM_COMMAND,IDM_EINSTELLUNGEN,0L);
         return 0;

    case WM_NCCALCSIZE:              // Set client area to full window area (enables total screen drawing)
         if (glo_adj.crosspainting)  // Yes, allowed to paint whole screen black
         {
           return 0;
         }
         break;

    case WM_SIZE:
    case WM_LBUTTONDOWN:             // Inform stripchart of a possible unrevealing of its client area by popup menu
         if (hwndStrip)              // Stripchart on
           SendMessage(hwndStrip,WM_SIZE,0,0L);
         break;

    case WM_BLACKPAINT:  // Paint the whole screen black
         {
           HDC    hdc;
           HBRUSH hbr = CreateSolidBrush(RGB(0,0,0));
           HRGN   hrgn;

           // Note: WM_NCCALCSIZE must return 0 for this to paint the whole screen
           SetWindowPos(hWnd,0,0,0,0,0,SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOREDRAW);
           ShowWindow(hWnd,SW_SHOWMAXIMIZED);

           // The SetWindowPos call does not do the whole job, so:
           hdc=CreateDC("DISPLAY",NULL,NULL,NULL);
           hrgn=CreateRectRgn(0,0,GetDeviceCaps(hdc,HORZRES),GetDeviceCaps(hdc,VERTRES));
           FillRgn(hdc,hrgn,hbr);

           DeleteDC(hdc);
           DeleteObject(hrgn);
           DeleteObject(hbr);
         }
         return 0;

    case WM_COMMAND:
         switch (wParam)
         {
           case IDM_RUNEXP:
                {
                  BOOL everythingPrepared = FALSE;

                  if (!DoesPlayerRespondJVC("COM2")) // Konvention: Player auf COM2
                  {
                    MessageBox(hWnd,
                               "Prüfen Sie den Player und Versuchen Sie es nochmal",
                               "Keine Verbindung zum Videoplayer",
                               MB_OK|MB_TASKMODAL);
                    return 0;
                  }

                  if (MessageBox(hWnd,
                                 "Taste <ESC> bricht den Versuchslauf jederzeit ohne Vorwarnung ab",
                                 "Wirklich jetzt starten ?",
                                 MB_YESNO|MB_DEFBUTTON1|MB_TASKMODAL) == IDYES)
                  {
                    everythingPrepared = TRUE;
                  }

                  if (everythingPrepared)
                  {
                    GetClientRect(hWnd,&rectClient);

                    hwndExperiment = CreateWindow(invisibleClass,
                                     "Experiment läuft",
                                     WS_CHILDWINDOW |
                                     WS_CAPTION|                        // Child
                                     WS_VISIBLE|                        // Visible
                                     WS_SYSMENU,
                                     rectClient.left,
                                     rectClient.top,
                                     rectClient.right,
                                     rectClient.bottom,
                                     hWnd,                              // Parent window´s handle
                                     NULL,                              // Unique identifier
                                     GetWindowWord(hWnd,GWW_HINSTANCE), // handle to module
                                     NULL);

                    if(hwndExperiment == NULL)
                    {
                      LoadString(hInst,IDS_ERR_CREATE_WINDOW,szString,sizeof(szString));
                      MessageBox(NULL,szString,NULL,MB_ICONEXCLAMATION);
                      return IDS_ERR_CREATE_WINDOW;
                    }

                    // Kritische Diskussion der Timer bei Marcellus Buchheit S.523 ff wird als bekannt vorausgesetzt.
                    if (!SetTimer(hwndExperiment,T_ID_EXPERIMENT,ELAPSETIME,NULL))
                    {
                      MessageBox(hWnd,"Zu viele Timer aktiv","Timer-Problem",MB_ICONEXCLAMATION);
                      return FALSE;
                    }
                  }
                }
                return 0;

           case IDM_EINSTELLUNGEN:
                {
                  FARPROC lpfnConfigure;
                  char wholeName[13];


                  lpfnConfigure=MakeProcInstance((FARPROC)ConfigureMsgProc,hInst);
                  nRc=DialogBox(hInst,(LPSTR)"CONFIGURATION",hWnd,lpfnConfigure);
                  FreeProcInstance(lpfnConfigure);
                  // Für die Namensübernahme ist es egal, ob der sich inzwischen geändert hat
                  _fmemset(wholeName,'\0',sizeof(wholeName));
                  lstrcpy(wholeName,glo_adj.expname);
                  AddExt(wholeName,".DAT");
                  _fmemcpy(MonitorCMD_StartRecording+20,wholeName,13);
                }
                return 0;

           case IDM_S_BANDSTELLEANFAHREN:
                {
                  FARPROC lpfnSU_BANDMsgProc;

                  lpfnSU_BANDMsgProc=MakeProcInstance((FARPROC)SU_BANDMsgProc,hInst);
                  nRc=DialogBox(hInst,(LPSTR)"suchen",hWnd,lpfnSU_BANDMsgProc);
                  FreeProcInstance(lpfnSU_BANDMsgProc);
                }
                return 0;

           case IDM_S_KANAL0:
                ChartRequired=LENK; // Global identifier used by window procedure
                goto OPENSTRIP;
           case IDM_S_KANAL1:
                ChartRequired=GASP;
                goto OPENSTRIP;
           case IDM_S_KANAL2:
                ChartRequired=BREM;
                goto OPENSTRIP;
           case IDM_S_HBT:
                ChartRequired=HBTC;
                goto OPENSTRIP;
           case IDM_S_LGB:
                ChartRequired=LGBC;
                goto OPENSTRIP;
           case IDM_S_ALLES:
                ChartRequired=ALLC;
                goto OPENSTRIP;
OPENSTRIP:
                if (isStripOn)
                  SendMessage(hwndStrip,WM_CLOSE,0,0L); // Destroy previous chart

                isStripOn=TRUE; // Make notice
                GetClientRect(hWnd,&rectClient);

                hwndStrip = CreateWindow(szStripClass,             // Window class name, global
                                "Hardwaretest: Inputdaten",
                                WS_CHILDWINDOW |
                                WS_CAPTION|
                                WS_VISIBLE|
                                WS_THICKFRAME|
                                WS_CLIPSIBLINGS|
                                WS_MINIMIZEBOX |
                                WS_MAXIMIZEBOX,
                                (int)((float)rectClient.right*0.05),
                                (int)((float)rectClient.bottom*0.05),
                                (int)((float)rectClient.right*0.90),
                                (int)((float)rectClient.bottom*0.90),
                                hWnd,                              // Parent window´s handle
                                NULL,                              // Unique identifier
                                GetWindowWord(hWnd,GWW_HINSTANCE), // handle to module
                                NULL);

                if(hwndStrip == NULL)
                {
                  LoadString(hInst,IDS_ERR_CREATE_WINDOW,szString,sizeof(szString));
                  MessageBox(NULL,szString,NULL,MB_ICONEXCLAMATION);
                  return IDS_ERR_CREATE_WINDOW;
                }

                FlashWindow(hwndStrip,1);                          // Let Sender look like active Window
                return 0;

           case IDM_S_ENDCHARTS:
                if (isStripOn)
                  SendMessage(hwndStrip,WM_CLOSE,0,0L); // Destroy active chart

                ReEnableOtherMenues();
                return 0;

           case IDM_R_STEUERUNGA:
                {
                  FARPROC lpfnRECORDERMsgProc;

                  lpfnRECORDERMsgProc=MakeProcInstance((FARPROC)RECORDERMsgProc,hInst);
                  nRc=DialogBox(hInst,(LPSTR)"recorderDirect",hWnd,lpfnRECORDERMsgProc);
                  FreeProcInstance(lpfnRECORDERMsgProc);
                }
                break;

           case IDM_H_HANDBUCH:
                MessageBox(hWnd,
                           "Eine Broschüre als Bedienunganleitung sollte ausliegen",
                           "Handbuch",
                           MB_APPLMODAL|MB_ICONINFORMATION);

                break;

           case IDM_H_INFO:
                {
                  FARPROC lpfnINFOMsgProc;

                  lpfnINFOMsgProc=MakeProcInstance((FARPROC)INFOMsgProc,hInst);
                  nRc=DialogBox(hInst,(LPSTR)"info",hWnd,lpfnINFOMsgProc);
                  FreeProcInstance(lpfnINFOMsgProc);
                }
                break;

           case IDM_EXIT:
                PostMessage(hWnd,WM_CLOSE,0,0L);
                break;

           default: return DefWindowProc(hWnd,Message,wParam,lParam);
         }
         return 0;        // End of WM_COMMAND

    case WM_MOVE:
         return 0;

    case WM_SYSCOLORCHANGE:
         Ctl3dColorChange();
         return 0;

    case WM_PAINT:
         memset(&ps,0x00,sizeof(PAINTSTRUCT));
         hDC = BeginPaint(hWnd, &ps);
         SetBkMode(hDC,TRANSPARENT);
         EndPaint(hWnd,&ps);
         return 0;       //  End of WM_PAINT

    case WM_CLOSE:
         DestroyWindow(hwndStrip);
         DestroyWindow(hWnd);

         if (hWnd == hWndMain)
           PostQuitMessage(0);

         return 0;

    default: break;
  }
  return DefWindowProc(hWnd,Message,wParam,lParam);
} // End of WndProc


// Experiment child window procedure

LONG FAR PASCAL TheExpWndProc(HWND hWnd,WORD Message,WORD wParam,LONG lParam)
{
  static const int   itson=1;                 // Hilfskonstante zum Auslesen des DIO-Bytes
  static const int   itsoff=0;                // Hilfskonstante zum Auslesen des DIO-Bytes
  static int         brems_untruncated;       // 12-Bit A/D-Rohwert Bremse
  static long        Nmeasure;                // Laufende Nummer der Messung/Modelliteration
  static TIMEANDUSER timeanduser;             // Aktueller VITC (TC als packed BCD, user hexadezimal)
  static TIMEONLY    timecode;                // Nur Timecode Bytes, nicht User Bytes
  static int         speedOnFilm;             // Geschwindigkeit des Aufnahmefahrzeugs
  static int         lenkwOnFilm;             // Grad Lenkradstellung des Aufnahmefahrzeugs
  static int         lenkwPre;                // Segmentierter Lenkwinkel
  static INTFORMTC   firstTCThatCameIn;       // Erster TC, der tatsächlich gelesen wird
  static DWORD       startTime;               // Startzeit des Versuchslaufs (in Millisekunden)
  static BOOL        itsFirstScan;            // Ist beim ersten Scan TRUE
  static double      tachodisp;               // Tachoansteuerung, Wert für D/A-Wandler
  static int         l, t, r, b;              // left, top, right, bottom
  static RECT        rCl;                     // Client Rectangle für Fadenkreuzberechnungen
  static POINT       screenDimensP;           // Screen extension in pixels
  static POINT       screenDimensMM;          // Screen extension in millimeters
  static POINT       crossLoc;                // Where to set the target cross
  static int         addedNoise;              // Stör-Auslenkung (Sinus)
  static int         middleX;                 // Middle of display
  static double      piInRun;                 // Input for noise generation
  static double      ticsPerSecond;           // Number of timer tics received per Second
  static double      runIncrement;            // Inkrement für die Sinusfunktion (noise)
  static int         logicalLenkwIn;          // Lenkradstellung ohne Offset von 0
  static double      logicalScreenX;          // X-Koordinate auf theoretischem Bildschirmposition
  static WINKEL      headingAngleEffectIST;   // Inkrement/Dekrement, das diese Iteration beiträgt
  static WINKEL      headingAngleEffectSOLL;  // dito, aber für den Film (Soll-Heading)
  static WINKEL      headingAngle;            // Spiegelstellung in Winkelgrad
  static double      steeringWheelAngle;      // Lenkradstellung in Winkelgrad
  static COMSTAT     status;                  // COMSTAT zum Abfragen des Schittstellenstatus
  static double      turbinenDrehzahl;        // Simulierte Motordrehzahl
  static double      motorLast;               // prozentuale Last des Motors
  static double TransmitChannels[CHANNELMAX]; // Zwischenablage der Daten in double-Form
  static BYTE outBuffer[SZTRANSMITQUEUE];     // Zwischenpuffer zum Senden an EDV
  static int         nWritten;                // Seriell: Anzahl tatsächllich geschriebener Bytes
  static int         commErr;                 // Speichert Returnwert von GetCommError
  static HWND        hwndInstruments[15];     // Window handles für die Analoginstrumente
  static BOOL        startPositionFound;      // Sagt WM_TIMER-case, ob er freigeschaltet ist
  static double      lenkFilter[17];          // Filter für Film-Lenkwinkeldaten
  static double      vFilter[17];             // Filter für Film-Geschwindigkeitsdaten
  static double      gasFilter[17];           // Filter für Film-Drosselklappendaten
  static double      bremsFilter[17];         // Filter für Film-Bremspedaldruckdaten
  static double      gasPerCent;              // Septembermodell
  static double      bremsPedalKraft;         // 0 bis 24 Kilopond
  static double      filterIntegral;          // Gewichtete Summe der Abtastungen im Filter
  static double      divisor = 1022+1020+1016+1008+992+960+896+768+512+256+128+64+32+16+8+4+2; // Filter-Divisor
  static double      turbinenDrehmoment;      // laut Modell
  static double      motorDrehzahlFake;       // Eine gehörrichtige "Motordrehzahl"
  static double      antriebsKraft;           // laut Modell
  static double      normedSpeedInLastScan;   // Bei der letzten Iteration gefahrene Geschwindigkeit
  static int         emergencyStopBreaksCnt;  // Wieviele Notbremsiterationen sind noch auszuführen
  int                renormedBrems;           // Bremspedalkraft in Prozent

  switch (Message)
  {
    case WM_CREATE:
         // Tell BRS525E.DLL to connect to video player
         if (!SetupCommunicationJVC("COM2"))
         {
           MessageBeep(MB_ICONHAND);
           MessageBox(NULL,
                      "Bitte Verbindungsleitungen prüfen",
                      "Kann Verbindung zum Videoabspieler über COM2 nicht aufbauen",
                      MB_OK|MB_ICONHAND|MB_TASKMODAL);
           goto SUDDENDEATH;
         }
         else
         {
           startPositionFound=FALSE; // WM_TIMER-case darf noch nichts tun
           tape_to_start();          // Advance tape to start position
           // Freischaltung ist letzter Schritt von WM_CREATE
         }

         if (glo_adj.do_sendserial)
         {
           // Open serial port for data transmission
           if (!SetupCommunicationEDV("COM4"))
           {
             MessageBeep(MB_ICONHAND);
             MessageBox(NULL,
                        "Bitte Verbindungsleitungen prüfen",
                        "Kann Verbindung zum Datenaufzeichner über COM4 nicht aufbauen",
                        MB_OK|MB_ICONHAND|MB_TASKMODAL);
             goto SUDDENDEATH;
           }
           else
           {
             // Kommando an EDV, sich vorzubereiten auf Empfang
             nWritten=WriteComm(Com.TheComID,MonitorCMD_StartRecording,SZTRANSMITQUEUE);

             if (nWritten != SZTRANSMITQUEUE)
             {
               InternalErrorBox(IDS_MONITORSTARTRECORDINGERR);
               goto SUDDENDEATH;
             }
           }
         }

         if (glo_adj.run_mirror)
         {
           // Open serial port for mirror driver
           if (!MIRRORSetupCommunication("COM3"))
           {
             MessageBeep(MB_ICONHAND);
             MessageBox(NULL,
                        "Bitte Verbindungsleitungen prüfen",
                        "Kann Verbindung zum Spiegelsteuergerät über COM3 nicht aufbauen",
                        MB_OK|MB_ICONHAND|MB_TASKMODAL);
             goto SUDDENDEATH;
           }
           else
           {
             MIRRORPosMiddle(); // Zurücksetzen
           }
         }

         ShowCursor(FALSE); // Wird aus ästhetischen Gründen schon früh versteckt
         DumpLogInfo();

         GetClientRect(hWnd,&rCl);
         l=(int)((float)rCl.right*0.20);
         t=(int)((float)rCl.bottom*0.05);
         r=(int)((float)rCl.right*0.95);
         b=(int)((float)rCl.bottom*0.95);

         memset(lenkFilter,0x00,sizeof(lenkFilter));
         Nmeasure=0;
         headingAngle=0.;
         headingAngleEffectIST=0.;
         headingAngleEffectSOLL=0.;
         currentSeg = LEFTSEG;                     // Annahme: Startsegment ist linkes
         lastLW = 52;                              // Annahme: Wir fahren geradeaus

         dt_initialize();                          // Initialize Data Translation board
         installreader();                          // And PCL-3
         delay(9000000); // ca. 1.5 Sekunden: "Monitor" muß MonitorCMD_StartRecording verarbeiten !
         tape_to_start();                          // Advance tape to start position
         PlayJVC();  // Start Player to read timecodes

         // Paint the whole screen black
         glo_adj.crosspainting=1; // Set manipulate-WM_NCCALCSIZE to on
         SendMessage(GetParent(hWnd),WM_BLACKPAINT,0,0L);

         // Note: WM_NCCALCSIZE must return 0 for this to paint the whole screen
         SetWindowPos(hWnd,0,0,0,0,0,SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOREDRAW);
         ShowWindow(hWnd,SW_SHOWMAXIMIZED);

         // Initialisierungen für die neue Tracking bzw. Lenkaufgabe
         if (glo_adj.run_cursor) // Fadenkreuzaufgabe aktiv
         {
           HDC hdc;
           POINT cursorExt;

           cursorExt.x=GetSystemMetrics(SM_CXCURSOR);
           cursorExt.y=GetSystemMetrics(SM_CYCURSOR);

           hdc=GetDC(hWnd);
           screenDimensP.x  = GetDeviceCaps(hdc,HORZRES);
           screenDimensP.y  = GetDeviceCaps(hdc,VERTRES);
           screenDimensMM.x = GetDeviceCaps(hdc,HORZSIZE);
           screenDimensMM.y = GetDeviceCaps(hdc,VERTSIZE);
           ReleaseDC(hWnd,hdc);
           middleX = screenDimensP.x / 2;
           crossLoc.y = screenDimensP.y - 2*cursorExt.y;  // Über der Motorhaube

           // Some crucial initializations
           rCl.left   = cursorExt.x / 2;
           rCl.top    = 0;
           rCl.right  = screenDimensP.x - (cursorExt.x / 2);
           rCl.bottom = screenDimensP.y;
           ClipCursor(&rCl);                            // Constrain cursor to screen
           SetCursorPos(screenDimensP.x/2,screenDimensP.y/2);
         }

         // Analoginstrumente werden benutzt, das ist extrem Rechenzeitaufwendig
         if (TRUE) // Immer, Zeit reicht
         {
           RECT client;
           GetClientRect(hWnd,&client);
           CreateControlInstruments(hwndInstruments,hWnd,hInst,&client);
         }

         EnableHardwareInput(FALSE); // Completely ignore operator`s actions (except: <ESC>)
         itsFirstScan = TRUE;
         startPositionFound = TRUE;
         return 0;

    case WM_NCCALCSIZE:              // Set client area to full window area (enables total screen drawing)
         if (glo_adj.crosspainting)  // Yes, allowed to paint whole screen black
         {
           return 0;
         }
         break;

    case WM_TIMER:

         if (!startPositionFound)    // Der Player sucht noch die Startstelle auf dem Band
           return 0;

#ifdef MEASURE_SPARETIME
         MESPstartTime = timeGetTime();
#endif
         Nmeasure++; // The trial counter

         requesttc();

         do
         { // nothing but awaiting completion of tc read operation
           // Allow for emergency exit
           if (IsEscapeCondition()) // <ESC> pressed
           {
             goto SUDDENDEATH;
           }
         } while (!GetTC(&timeanduser));

         timecode.bcdSt = timeanduser.bcdSt;
         timecode.bcdMi = timeanduser.bcdMi;
         timecode.bcdSe = timeanduser.bcdSe;
         timecode.bcdFr = timeanduser.bcdFr;

///////////////////////////////////////////////////////////////////////////////////////
// Das ist die Mobinet-Version, sie liest ungefilterte BCD-Daten von M2-Application

         // Lenkwinkel glätten
         // Filter um 1 shiften
         memmove(lenkFilter+1,lenkFilter,16*sizeof(double));
         // Aktuellen Wert an die nun freie höchstbewertete Stelle des Filters setzen
         lenkFilter[0] = Renorm(GetIntFromJankerBCD(timeanduser.us7_8,timeanduser.us5_6),0,230,-720,720);
         // Aufsummieren und dabei mit Funktion falten
         filterIntegral = lenkFilter[0]*1022 +
                          lenkFilter[1]*1020 +
                          lenkFilter[2]*1016 +
                          lenkFilter[3]*1008 +
                          lenkFilter[4]*992. +
                          lenkFilter[5]*960. +
                          lenkFilter[6]*896. +
                          lenkFilter[7]*768. +
                          lenkFilter[8]*512. +
                          lenkFilter[9]*256. +
                          lenkFilter[10]*128. +
                          lenkFilter[11]*64. +
                          lenkFilter[12]*32. +
                          lenkFilter[13]*16. +
                          lenkFilter[14]*8. +
                          lenkFilter[15]*4. +
                          lenkFilter[16]*2.;
         // Division zum Resultat
         lenkwOnFilm =(int)(filterIntegral/divisor);
///////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////
// formerly:
//         // Kanal 2: 8 Bit Geschwindigkeit, kann direkt übernommen werden
//         // Geschwindigkeit glätten
//         // Filter um 1 shiften
//         memmove(vFilter+1,vFilter,16*sizeof(double));
//         // Aktuellen Wert an die nun freie höchstbewertete Stelle des Filters setzen
//         vFilter[0] = RebuildIntegerFromByte(timeanduser.us5_6);
//         // Aufsummieren und dabei mit Funktion falten
//         filterIntegral = vFilter[0]*1022 +
//                          vFilter[1]*1020 +
//                          vFilter[2]*1016 +
//                          vFilter[3]*1008 +
//                          vFilter[4]*992. +
//                          vFilter[5]*960. +
//                          vFilter[6]*896. +
//                          vFilter[7]*768. +
//                          vFilter[8]*512. +
//                          vFilter[9]*256. +
//                          vFilter[10]*128. +
//                          vFilter[11]*64. +
//                          vFilter[12]*32. +
//                          vFilter[13]*16. +
//                          vFilter[14]*8. +
//                          vFilter[15]*4. +
//                          vFilter[16]*2.;
//         // Division zum Resultat
//         speedOnFilm =(int)(filterIntegral/divisor);
//         // Geschwindigkeiten kleiner 5 km/h zählen als Stillstand
///////////////////////////////////////////////////////////////////////////////////////

         speedOnFilm = (int)Renorm(GetIntFromJankerBCD(timeanduser.us3_4,timeanduser.us1_2),0,200,0,100);

         if (speedOnFilm < 5)
         {
           speedOnFilm = 0;
         }

         // Kanal 3: 8 Bit Drosselklappenstellung wird hier nicht gebraucht

         // Kanal 4: 8 Bit Bremspedaldruck wird hier nicht gebraucht

         if (itsFirstScan)
         {
           firstTCThatCameIn = BCDTC2IntFormatTc(timecode);
           startTime = timeGetTime();

           // Modell auf 0 setzen, es wird immer aus dem Stand begonnen
           SetModelQuiescence();

           // Analoginstrumente benutzen, das ist extrem Rechenzeitaufwendig
           if (TRUE) // Immer, Zeit müßte reichen
           {
             // Sichtbarmachen der AnalogInstrumente
             ShowWindow(hwndInstruments[ANA_LENKRAD_FILM],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_LENKRAD_VP],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_GAS_FILM],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_GAS_VP],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_BREMSE_FILM],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_BREMSE_VP],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_V_FILM],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_V_VP],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_HEADING_FILM],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_HEADING_VP],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_VAR_TRACK],SW_SHOW);
             ShowWindow(hwndInstruments[ANA_MIRROR_POS],SW_SHOW);
           }

           if (glo_adj.run_cursor)
           {
             // Wieder sichtbar machen (wird vor Aufruf von TheExpWndProc schon versteckt,
             // denn es sieht sehr seltsam aus, wenn er nicht gebraucht wird und spät verschwindet
             ShowCursor(TRUE);
           }

           normedSpeedInLastScan = 0;
           emergencyStopBreaksCnt = 0;

           itsFirstScan = FALSE;
         }

         // Software timimg used at the moment !!!
         // Look at the caller, I´m using a Windows "timer"
         // Precision is bad, but it´s a very stable pure software solution.
         // The DT2811 "clock" is nevertheless involved, because I try to
         // speed up things on the DT2811 by running it in "Mode 1", "Mode 0"
         // operations can be terribly slow. This explanation might seem a
         // little bit strange to you. Programming a DT2811 board is a strange
         // thing, indeed. The situation will get clearer, if you read the
         // DT2811 manual, sometimes the board´s behaviour actually matches
         // this description - if not, good luck !

         digital_in=dt_get_digital_in();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         lenkw_in=dt_get_channel_0();
         dt_disable_clock();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         gaspd_in=dt_get_channel_1();
         dt_disable_clock();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         brems_untruncated=brems_in=dt_get_channel_2();
         dt_disable_clock();

         // Für allgemeine Zwecke, die Fadenkreuzaufgabe geht hier einen anderen Weg
         // Minimum und Maximum sind empirisch beobachtet nach Tarieren
         steeringWheelAngle = Renorm((double)lenkw_in,528.,3575.,-540.,540.);

         // Geschwindigkeits-Subsystem:

         // Der Kickdownpunkt wurde empirisch gesucht und zu 100 Prozent durchgetreten (= 1.0) normiert,
         // alles was darüber hinausgeht, zählt als Kick-Down (bei 100 Prozent Gas schaltet das Modell
         // in HARTWICH.DLL anders als im Bereich vor dem Kick-Down.
         // Der Kick-Down-Punkt ist bei 3500 erreicht, somit ist er bei 3550 auf jeden Fall
         // durchgetreten. Größere Werte werden schon im Urbildbereich vernachlässigt.
         gasPerCent=Renorm((double)gaspd_in,2048.,3550.,0.,100.-STANDGAS);

         // Standgas dazuaddieren.
         gasPerCent += STANDGAS;

         if (brems_in < 2050) // Noise Abschneiden, um nur wirkliches Bremsen zu bekommen
           brems_in=0;

         // Input-Maximum muß feinjustiert werden, weil niemand die Bremse voll drücken kann. Das Modell
         // in der HARTWICH.DLL erwartet Bremspedalkräfte bis zu 24 kp, daher die Normierung.

         bremsPedalKraft=Renorm((double)brems_in,2050.,2750.,0.,24.);

         // Bei Kollision (zu weites Abkommen vom Sollenken) wird eine Notbremsung durchgeführt

         if (emergencyStopBreaksCnt != 0) // Notbremsung im Gange
         {
           UpdateModel(10.,20.);
           emergencyStopBreaksCnt--;
         }
         else
         {
           // Aufruf des Modells
           UpdateModel(gasPerCent,bremsPedalKraft);
         }

         NormedSpeed = ModeledFahrgeschwindigkeit();

         // Berücksichtigung der Stand-Anomalie für das Geschwindigkeits-Subsystem - wenn im Film ein Stand
         // vorkommt, steht auch die Simulation zwangsweise, diese Pause wird also in Echtzeit absolviert.
         if (speedOnFilm == 0)
         {
           NormedSpeed = 0;
         }

         if (glo_adj.run_tacho)
         {
           tachodisp=NormedSpeed*TACHONORMIERUNG; // Der Faktor ist empirisch ermittelt
           dt_send_channel_0((int)tachodisp);
         }

         if (speedOnFilm == 0) // Gibt division by zero und ist Standanomalie
         {
           SetModelQuiescence(); // Standzeit im Film: Kontrolle über das Modell ist ausgesetzt
           PlayJVC();
         }
         else
         {
           // Film mit gesteuerter Geschwindigkeit laufen lassen
           SetVarJVC((float)NormedSpeed,(float)speedOnFilm);
         }

         // Drehzahl und Lastzustand des Motors, wird vom Farzzeuggeräuschsimulator achilles85
         // der TU München gebraucht

         // Als Drehzahl wird die im Hartwich-Modell vorkommende Turbinendrehzahl verwendet
         turbinenDrehzahl = ModeledTurbinendrehzahl();

         // Die "Motordrehzahl" ist nur für die fahrgeräuschsimulation relevant.
         // Sie wird gehörrichtig eingestellt, nicht nach zu erwartenden Motordrehzahlen.
         motorDrehzahlFake = Renorm(turbinenDrehzahl,0.,5000.,2000.,4000);

         // Der Lastzustand des Motors ist dann hoch, wenn ein hohes Drehmoment eine geringe
         // Antriebskraft erzeugt (ein direkter übersetzender Gang ist dann geschaltet)
         // turbinenDrehmoment = ModeledTurbinendrehmoment();
         // antriebsKraft = ModeledAntriebskraft();

         // Monitor erwartet hier einen Prozentwert
         motorLast = PercentModeledMotorLast();

         // Cursoraufgabe und Spiegelaufgabe benutzen das gleiche Integrator-
         // system. Ein Lenkfehler (= Abweichung der Lenkradstellung von der Film-
         // Lenkradstellung) wirkt sich dabei auf die theoretische Größe HaedingAngle
         // aus. Die Pseudogeschwindigkeitwirkt sich dabei dadurch aus, daß die
         // berücksichtigten Abweichungen umso schneller auftreten (= die Film-
         // Lenkradstellungen sich umso schneller ändern), je schneller man "fährt".

         // Subsystem laterale Kontrolle (benötigt das gerade berechnete NormedSpeed)

         // Zeigt, wie das ganze mal gedacht war, momentan zählt die Methode von glo_adj.run_mirror <<<<<
         if (glo_adj.run_cursor)
         {
           // Lenkfehler für diesen Durchlauf
           headingAngleEffectIST = (steeringWheelAngle - lenkwOnFilm) * HEADING_EFFECT_OF_1_DEGREE; // IST
           // Auswirkung auf die Spiegelauslenkung/Cursorpositionierung
           if (NormedSpeed != 0)
           {
             headingAngle += headingAngleEffectIST;
           }
           // X-Position des Cursors
           crossLoc.x = (screenDimensP.x / 2) + (int)(headingAngle * NUM_PIX_MAKING_5CENTIMETERS);

           // Spiegelpositionierungswinkel/Cursorpositionierungswinkel anwenden auf Cursor
           SetCursorPos(crossLoc.x,crossLoc.y);
         }

         if (glo_adj.run_mirror) // Lenkrückmeldung mit Spiegel
         {
           double averageSpeed = (NormedSpeed+normedSpeedInLastScan) / 2.;
           double metersPerMSECSpeed = averageSpeed / 3600.;
           double metersMade = metersPerMSECSpeed * ELAPSETIME;
           double headingError = steeringWheelAngle - lenkwOnFilm;

           // Lenkfehler für diesen Durchlauf
           headingAngleEffectIST = headingError * metersMade * HEADING_EFFECT_OF_1_DEGREE;

           // Auswirkung auf die Spiegelauslenkung/Cursorpositionierung
           if (NormedSpeed != 0)
           {
             headingAngle += headingAngleEffectIST; // Am besten bewährt
           }

           // Anschlagbegrenzung (für Funktionsfähigkeit erforderlich)
           if (headingAngle > -20. && headingAngle < 20.)
           {
             // Spiegelpositionierung über Microcontrollergerät an COM3 ansteuern
             MIRRORPosAbsolute(-headingAngle);
           }

           // Notrückstellung, falls es in den Graben gegangen ist
           if (headingAngle < -20. || headingAngle > 20.)
           {
             if (emergencyStopBreaksCnt == 0)
             {
               emergencyStopBreaksCnt = 50; // Anzahl Iterationen für Kollisionsbremsung
             }
           }

           if (emergencyStopBreaksCnt == 1) // Kollisionswarnungsbremsung gleich beendet
           {
             headingAngle = 0.;
             MIRRORPosMiddle(); // Spiegel zurückstellen
           }

           normedSpeedInLastScan = NormedSpeed;
         }

         // Sollgeschwindigkeit dieses Durchlaufs für den nächsten vermerken, damit werden
         // Eintrittspunkt in und Austrittspunkt aus Standanomalie erkannt

         // Analoginstrumente updaten, das ist extrem Rechenzeitaufwendig
         if (TRUE) // Immer, Zeit reicht
         {
           int take_brems_in;
           int percentTrackSpeed;
           int gasVomVideo;
           int bremseVomVideo;

           renormedBrems = (int)Renorm(bremsPedalKraft,0.,24.,0.,100.);
           // Für Analoganzeigen sind Prozentskalierungen am praktischsten
           // Drosselklappenstellung glätten
           memmove(gasFilter+1,gasFilter,16*sizeof(double));
           gasFilter[0] = Renorm(RebuildIntegerFromByte(timeanduser.us1_2),0,152,0,100);
           filterIntegral = gasFilter[0]*1022 +
                            gasFilter[1]*1020 +
                            gasFilter[2]*1016 +
                            gasFilter[3]*1008 +
                            gasFilter[4]*992. +
                            gasFilter[5]*960. +
                            gasFilter[6]*896. +
                            gasFilter[7]*768. +
                            gasFilter[8]*512. +
                            gasFilter[9]*256. +
                            gasFilter[10]*128. +
                            gasFilter[11]*64. +
                            gasFilter[12]*32. +
                            gasFilter[13]*16. +
                            gasFilter[14]*8. +
                            gasFilter[15]*4. +
                            gasFilter[16]*2.;
           gasVomVideo = (int)(filterIntegral / divisor);

           // Bremspedaldruck glätten
           memmove(bremsFilter+1,bremsFilter,16*sizeof(double));
           bremsFilter[0] = Renorm(RebuildIntegerFromByte(timeanduser.us3_4),0,134,0,100);
           filterIntegral = bremsFilter[0]*1022 +
                            bremsFilter[1]*1020 +
                            bremsFilter[2]*1016 +
                            bremsFilter[3]*1008 +
                            bremsFilter[4]*992. +
                            bremsFilter[5]*960. +
                            bremsFilter[6]*896. +
                            bremsFilter[7]*768. +
                            bremsFilter[8]*512. +
                            bremsFilter[9]*256. +
                            bremsFilter[10]*128. +
                            bremsFilter[11]*64. +
                            bremsFilter[12]*32. +
                            bremsFilter[13]*16. +
                            bremsFilter[14]*8. +
                            bremsFilter[15]*4. +
                            bremsFilter[16]*2.;
           bremseVomVideo = (int)(filterIntegral / divisor);
           // Bremspedaldruck kleiner 10 Prozent ist wahrscheinlich reiner Noise, wird abgeschnitten
           if (bremseVomVideo < 10)
           {
             bremseVomVideo = 0;
           }

           if (speedOnFilm == 0)
             percentTrackSpeed = 100;
           else
             percentTrackSpeed = (int)(100. * (NormedSpeed / (double)speedOnFilm));

           SendMessage(hwndInstruments[ANA_LENKRAD_FILM],AMM_SETVALUE,lenkwOnFilm,0L);
           SendMessage(hwndInstruments[ANA_LENKRAD_VP],AMM_SETVALUE,(int)steeringWheelAngle,0L);
           SendMessage(hwndInstruments[ANA_GAS_FILM],AMM_SETVALUE,gasVomVideo,0L);
           SendMessage(hwndInstruments[ANA_GAS_VP],AMM_SETVALUE,(int)gasPerCent,0L);
           SendMessage(hwndInstruments[ANA_BREMSE_FILM],AMM_SETVALUE,bremseVomVideo,0L);
           SendMessage(hwndInstruments[ANA_BREMSE_VP],AMM_SETVALUE,renormedBrems,0L);
           SendMessage(hwndInstruments[ANA_V_FILM],AMM_SETVALUE,speedOnFilm,0L);
           SendMessage(hwndInstruments[ANA_V_VP],AMM_SETVALUE,(int)NormedSpeed,0L);
           SendMessage(hwndInstruments[ANA_HEADING_FILM],AMM_SETVALUE,(int)headingAngleEffectSOLL,0L);
           SendMessage(hwndInstruments[ANA_HEADING_VP],AMM_SETVALUE,(int)headingAngleEffectIST,0L);
           SendMessage(hwndInstruments[ANA_VAR_TRACK],AMM_SETVALUE,percentTrackSpeed,0L);
           SendMessage(hwndInstruments[ANA_MIRROR_POS],AMM_SETVALUE,(int)headingAngle,0L);
         }

         if (glo_adj.do_sendserial)
         {
           // Versenden der Daten an Programm "Monitor" auf dem Aufzeichnungsrechner

           commErr=0; // Clear

           if (!Com.SetUpOK)
           {
             InternalErrorBox(IDS_COMPORTNOTSETUP);
             goto SUDDENDEATH;
           }
           else
           {
             commErr=GetCommError(Com.TheComID,&status);

             if (commErr)
             {
               ExplainCommError(commErr);
               goto SUDDENDEATH;
             }
             else
             {
               if (status.cbOutQue == 0) // Letzter Chunk fertig gesendet
               {
                 INTFORMTC currentTC = BCDTC2IntFormatTc(timecode);

                 // Die Daten werden immer komplett gesendet, egal ob angekreuzt oder nicht

                 // Sollenkradstellung auf Kanal 0
                 TransmitChannels[0]  = (double)-lenkwOnFilm;

                 // Lenkradstellung auf Kanal 1
                 TransmitChannels[1]  = (double)-steeringWheelAngle;

                 // Sollgeschwindigkeit auf Kanal 2
                 TransmitChannels[2]  = (double)speedOnFilm;

                 // Pseudogeschwindigkeit auf Kanal 3
                 TransmitChannels[3]  = NormedSpeed;

                 // Gaspedalstellung auf Kanal 4
                 TransmitChannels[4]  = gasPerCent;

                 // Bremspedaldruck auf Kanal 5
                 TransmitChannels[5]  = renormedBrems;

                 // Spiegelauslenkung auf Kanal 6
                 TransmitChannels[6]  = headingAngle;

                 // Fadenkreuzauslenkung auf Kanal 7
                 TransmitChannels[7]  = -headingAngle; // Zu weit rechts sein == negativ

                 // Videotimecode in Sekunden auf Kanal 8
                 TransmitChannels[8]  = (double)VideoSecondsMade(firstTCThatCameIn,currentTC);

                 // Echtzeit in Sekunden auf Kanal 9 (Millisekundenpräzision)
                 TransmitChannels[9]  = (double)(timeGetTime() - startTime) / 1000.;

                 // Wenn für Zeitmeßlauf kompiliert, enthält der Kanal die verbrauchte Zeit für eine Iteration
#ifdef MEASURE_SPARETIME
                 TransmitChannels[9]  = (double)iterationTakesMSECS;
#endif

                 // Hupe auf Kanal 10
                 TransmitChannels[10] = (double)(digital_in & 0x01 ? itsoff : itson);

                 // Blinker links auf Kanal 11
                 TransmitChannels[11] = (double)(digital_in>>1 & 0x01);

                 // Blinker rechts auf Kanal 12
                 TransmitChannels[12] = (double)(digital_in>>2 & 0x01);

                 // Tempomathebel auf Kanal 13
                 TransmitChannels[13] = (double)(digital_in>>3 & 0x01);

                 // Das ist bis TransmitChannels[15] erweiterbar
                 TransmitChannels[14] = motorDrehzahlFake;

                 TransmitChannels[15] = motorLast;

                 // Letzte Werte zur Sicherheit löschen
                 memset(outBuffer,0x00,sizeof(outBuffer));

                 // Übertragen von der Datenausgabe nach "outBuffer"

                 // BYTE 0,1: Timecode Stunden
                 IntParts(currentTC.intSt,&outBuffer[0],&outBuffer[1]);
                 // BYTE 2,3: Timecode Minuten
                 IntParts(currentTC.intMi,&outBuffer[2],&outBuffer[3]);
                 // BYTE 4,5: Timecode Sekunden
                 IntParts(currentTC.intSe,&outBuffer[4],&outBuffer[5]);
                 // BYTE 6,7: Timecode Frames
                 IntParts(currentTC.intFr,&outBuffer[6],&outBuffer[7]);
                 // BYTE 8,9,10,11: Datum Kanal 0
                 FloatParts((float)TransmitChannels[0],&outBuffer[8],&outBuffer[9],&outBuffer[10],&outBuffer[11]);
                 // BYTE 12,13,14,15: Datum Kanal 1
                 FloatParts((float)TransmitChannels[1],&outBuffer[12],&outBuffer[13],&outBuffer[14],&outBuffer[15]);
                 // BYTE 16,17,18,19: Datum Kanal 2
                 FloatParts((float)TransmitChannels[2],&outBuffer[16],&outBuffer[17],&outBuffer[18],&outBuffer[19]);
                 // BYTE 20,21,22,23: Datum Kanal 3
                 FloatParts((float)TransmitChannels[3],&outBuffer[20],&outBuffer[21],&outBuffer[22],&outBuffer[23]);
                 // BYTE 24,25,26,27: Datum Kanal 4
                 FloatParts((float)TransmitChannels[4],&outBuffer[24],&outBuffer[25],&outBuffer[26],&outBuffer[27]);
                 // BYTE 28,29,30,31: Datum Kanal 5
                 FloatParts((float)TransmitChannels[5],&outBuffer[28],&outBuffer[29],&outBuffer[30],&outBuffer[31]);
                 // BYTE 32,33,34,35: Datum Kanal 6
                 FloatParts((float)TransmitChannels[6],&outBuffer[32],&outBuffer[33],&outBuffer[34],&outBuffer[35]);
                 // BYTE 36,37,38,39: Datum Kanal 7
                 FloatParts((float)TransmitChannels[7],&outBuffer[36],&outBuffer[37],&outBuffer[38],&outBuffer[39]);
                 // BYTE 40,41,42,43: Datum Kanal 8
                 FloatParts((float)TransmitChannels[8],&outBuffer[40],&outBuffer[41],&outBuffer[42],&outBuffer[43]);
                 // BYTE 44,45,46,47: Datum Kanal 9
                 FloatParts((float)TransmitChannels[9],&outBuffer[44],&outBuffer[45],&outBuffer[46],&outBuffer[47]);
                 // BYTE 48,49,50,51: Datum Kanal 10
                 FloatParts((float)TransmitChannels[10],&outBuffer[48],&outBuffer[49],&outBuffer[50],&outBuffer[51]);
                 // BYTE 52,53,54,55: Datum Kanal 11
                 FloatParts((float)TransmitChannels[11],&outBuffer[52],&outBuffer[53],&outBuffer[54],&outBuffer[55]);
                 // BYTE 56,57,58,59: Datum Kanal 12
                 FloatParts((float)TransmitChannels[12],&outBuffer[56],&outBuffer[57],&outBuffer[58],&outBuffer[59]);
                 // BYTE 60,61,62,63: Datum Kanal 13
                 FloatParts((float)TransmitChannels[13],&outBuffer[60],&outBuffer[61],&outBuffer[62],&outBuffer[63]);
                 // BYTE 64,65,66,67: Datum Kanal 14
                 FloatParts((float)TransmitChannels[14],&outBuffer[64],&outBuffer[65],&outBuffer[66],&outBuffer[67]);
                 // BYTE 68,69,70,71: Datum Kanal 15
                 FloatParts((float)TransmitChannels[15],&outBuffer[68],&outBuffer[69],&outBuffer[70],&outBuffer[71]);
                 // BYTE 72,73: Checksumme
                 IntParts(MakeChecksum(outBuffer,72),&outBuffer[72],&outBuffer[73]); // >>>>>

                 // Schreiben
                 nWritten=WriteComm(Com.TheComID,outBuffer,SZTRANSMITQUEUE);

                 if (nWritten != SZTRANSMITQUEUE)
                 {
                   InternalErrorBox(IDS_NUMWRITTENMISMATCH);
                   goto SUDDENDEATH;
                 }
               }
               else
               {
                 // Sendepuffer war noch nicht leer, passiert wenn EDV nicht liest
                 // Jeder muß wissen, daß Abtastungen verlorengehen, wenn dieser Ton
                 // zu hören ist
                 MessageBeep(MB_ICONHAND);
               }
             } // End else no commError
           } // End else serial communication is set up
         } // End if (SollenÜberhauptDatenVerschicktWerden)

         if (ItsTheEnd(&timecode))                          // Abort operation immediately
         {
SUDDENDEATH:
           SetModelQuiescence();                            // Modell zum Stehen bringen
           KillTimer(hWnd,T_ID_EXPERIMENT);                 // Kill timer immediately to stop execution
           StopJVC();
           tape_to_start();                                 // Back to the beginning, ready for next trial
           EnableHardwareInput(TRUE);
           glo_adj.crosspainting=0;                         // Set manipulate-WM_NCCALCSIZE to off
           SendMessage(GetParent(hWnd),WM_BLACKPAINT,0,0L); // and undo black-paint of whole screen
           ShutDownCommunicationJVC();                      // This is important

           if (glo_adj.do_sendserial)                       // EDV hängt am Kabel
           {
             do // Warten, bis der letzte Chunk fertig gesendet ist
             {
               GetCommError(Com.TheComID,&status);
             }
             while (status.cbOutQue != 0);

             // Monitor läuft auch auf langsamen Rechnern; das kann ohne die folgende Zeile ganz
             // zuletzt an einem ungewöhnlich kurzen Zeitintervall zwischen dem letzten Datenchunk
             // und dem Kommando-Chunk "MonitorCMD_EndRecording" scheitern, daher:
             delay(400000); // Warte noch etwas

             nWritten=WriteComm(Com.TheComID,MonitorCMD_EndRecording,SZTRANSMITQUEUE);

             if (nWritten != SZTRANSMITQUEUE)
             {
               InternalErrorBox(IDS_MONITORENDRECORDINGERR);
             }

             ShutDownCommunicationEDV();                      // This is important
           }

           if (glo_adj.run_mirror)
           {
             MIRRORShutDownCommunication();                   // This is important
           }

           if (!glo_adj.run_cursor) // Dann war der Cursor versteckt
             ShowCursor(TRUE); // Display count wieder auf 0 inkrementieren

           ClipCursor(NULL);
           DestroyWindow(hWnd);
         }

#ifdef MEASURE_SPARETIME
         MESPendTime = timeGetTime();
         iterationTakesMSECS = MESPendTime - MESPstartTime;
#endif
         return 0;

    case WM_COMMAND :
         return 0;

    case WM_CLOSE:
         SetModelQuiescence();                            // Modell zum Stehen bringen
         KillTimer(hWnd,T_ID_EXPERIMENT);
         StopJVC();
         tape_to_start(); // Back to the beginning, ready for next trial
         EnableHardwareInput(TRUE);
         glo_adj.crosspainting=0;                         // Set manipulate-WM_NCCALCSIZE to off
         SendMessage(GetParent(hWnd),WM_BLACKPAINT,0,0L); // And undo black-paint of whole screen
         ShutDownCommunicationJVC();                      // This is important

         if (glo_adj.do_sendserial)                       // EDV hängt am Kabel
         {
           do // Warten, bis der letzte Chunk fertig gesendet ist
           {
             GetCommError(Com.TheComID,&status);
           }
           while (status.cbOutQue != 0);

           delay(400000); // Erklärung siehe SUDDENDEATH:

           nWritten=WriteComm(Com.TheComID,MonitorCMD_EndRecording,SZTRANSMITQUEUE);

           if (nWritten != SZTRANSMITQUEUE)
           {
             InternalErrorBox(IDS_MONITORENDRECORDINGERR);
           }

           ShutDownCommunicationEDV();                      // This is important
         }

         if (glo_adj.run_mirror)
         {
           MIRRORShutDownCommunication();                   // This is important
         }

         if (!glo_adj.run_cursor) // Dann war der Cursor versteckt
           ShowCursor(TRUE); // Display count wieder auf 0 inkrementieren

         ClipCursor(NULL);
         DestroyWindow(hWnd);
         return 0;

    default: break;
  }
  return DefWindowProc(hWnd,Message,wParam,lParam);
} // End of WndProc


// Stripcharts child window procedure

LONG FAR PASCAL StripWndProc(HWND hWnd,WORD Message,WORD wParam,LONG lParam)
{
  HDC hDC;                                         // Handle for the display device
  static RECT rCl;
  PAINTSTRUCT ps;
  static HFONT hBackUpFont;
  static int l, t, r, b;                           // left, top, right, bottom
  static int room41;                               // room for one subchart
  static int higher;                               // used to make a subchart higher

  switch (Message)
  {
    case WM_CREATE:
         {
           DisableOtherMenues();
           // Kritische Diskussion der Timer bei Marcellus Buchheit S.523 ff wird als bekannt vorausgesetzt.
           if (!SetTimer(hWnd,T_ID_STRIPCHARTTIMING,55,NULL))
           {
             MessageBox(hWnd,"Nicht benötigte Anwendungen bitte schließen","Zu viele Timer aktiv",MB_ICONEXCLAMATION);
             return FALSE;
           }

           PostMessage(hWnd,WM_SIZE,0,0L);  // Must be PostMessage, not SendMessage !
           dt_initialize();                 // Initialize Data Translation board
         }
         break;

    case WM_MOVE:
         SendMessage(hWnd,WM_SIZE,0,0L);
         break;

    case WM_LBUTTONDOWN:                    // Possible unrevealing of client area by popup menu
    case WM_SIZE:
         {
           hDC=GetDC(hWnd);
           SetBkMode(hDC,TRANSPARENT);      // In case the background is not a pure color

           win3dFrame(hWnd,hDC);

           hBackUpFont=SelectObject(hDC,globalWorkFont);

           SetTextAlign(hDC,TA_RIGHT);

           GetClientRect(hWnd,&rCl);
           l=(int)((float)rCl.right*0.20);
           t=(int)((float)rCl.bottom*0.05);
           r=(int)((float)rCl.right*0.95);
           b=(int)((float)rCl.bottom*0.95);

           switch (ChartRequired)
           {
              case NOTH: break;

              case LENK: StripChart(hDC,0,l,t,r,b,0.,4096.,SETUP,"Lenk-Input");
                         break;

              case GASP: StripChart(hDC,1,l,t,r,b,0.,2048.,SETUP,"Gas-Input");
                         break;

              case BREM: StripChart(hDC,2,l,t,r,b,0.,2048.,SETUP,"Brems-Input");
                         break;

              case LGBC: room41=(b-t)/3;
                         higher=(int)((float)room41*0.1);
                         StripChart(hDC,0,l,t,r,room41+higher,0.,4096.,SETUP,"Lenk-Input");
                         StripChart(hDC,1,l,t+room41,r,2*room41+higher,0.,2048.,SETUP,"Gas-Input");
                         StripChart(hDC,2,l,t+2*room41,r,3*room41+higher,0.,2048.,SETUP,"Brems-Input");
                         break;

              case HBTC: room41=(b-t)/4;
                         higher=(int)((float)room41*0.1);
                         StripChart(hDC,5,l,t,r,room41+higher,0.,2.,SETUP,"Hupe");
                         StripChart(hDC,6,l,t+room41,r,2*room41+higher,0.,2.,SETUP,"Blinker links");
                         StripChart(hDC,7,l,t+2*room41,r,3*room41+higher,0.,2.,SETUP,"Blinker rechts");
                         StripChart(hDC,8,l,t+3*room41,r,4*room41+higher,0.,2.,SETUP,"Tempomat");
                         break;

              case ALLC: room41=(b-t)/7;
                         higher=(int)((float)room41*0.15);
                         StripChart(hDC,0,l,t,r,room41+higher,0.,4096.,SETUP,"Lenk-Input");
                         StripChart(hDC,1,l,t+room41,r,2*room41+higher,0.,2048.,SETUP,"Gas-Input");
                         StripChart(hDC,2,l,t+2*room41,r,3*room41+higher,0.,2048.,SETUP,"Brems-Input");
                         StripChart(hDC,5,l,t+3*room41,r,4*room41+higher,0.,2.,SETUP,"Hupe");
                         StripChart(hDC,6,l,t+4*room41,r,5*room41+higher,0.,2.,SETUP,"Blinker links");
                         StripChart(hDC,7,l,t+5*room41,r,6*room41+higher,0.,2.,SETUP,"Blinker rechts");
                         StripChart(hDC,8,l,t+6*room41,r,7*room41+higher,0.,2.,SETUP,"Tempomat");
                         break;

                default: break;
           }

           SelectObject(hDC,hBackUpFont);
           ReleaseDC(hWnd,hDC);
         }
         break;

    case WM_TIMER:
         {
           InvalidateRect(hWnd,NULL,FALSE); // Trigger next plot step
         }
         break;

    case WM_PAINT:
         memset(&ps,0x00,sizeof(PAINTSTRUCT));
         hDC=BeginPaint(hWnd,&ps);

         dt_set_clock_freq_max();

         digital_in=dt_get_digital_in();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         lenkw_in=dt_get_channel_0();
         dt_disable_clock();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         gaspd_in=dt_get_channel_1();
         dt_disable_clock();

         dt_set_clock_freq_max();
         dt_set_ad_mode();
         brems_in=dt_get_channel_2();
         dt_disable_clock();

         switch (ChartRequired) // Global indicator
         {
           case LENK: StripChart(hDC,0,0,0,0,0,(double)lenkw_in,4096.,PLOT,"");
                      break;

           case GASP: StripChart(hDC,1,0,0,0,0,(double)(gaspd_in-2048),2048.,PLOT,"");
                      break;

           case BREM: StripChart(hDC,2,0,0,0,0,(double)(brems_in-2048),2048.,PLOT,"");
                      break;

           case LGBC: StripChart(hDC,0,0,0,0,0,(double)lenkw_in,4096.,PLOT,"");
                      StripChart(hDC,1,0,0,0,0,(double)(gaspd_in-2048),2048.,PLOT,"");
                      StripChart(hDC,2,0,0,0,0,(double)(brems_in-2048),2048.,PLOT,"");
                      break;

           case HBTC: StripChart(hDC,5,0,0,0,0,(double)(digital_in & 0x01 ? 0 : 1),2.,PLOT,"");
                      StripChart(hDC,6,0,0,0,0,(double)(digital_in>>1 & 0x01),2.,PLOT,"");
                      StripChart(hDC,7,0,0,0,0,(double)(digital_in>>2 & 0x01),2.,PLOT,"");
                      StripChart(hDC,8,0,0,0,0,(double)(digital_in>>3 & 0x01),2.,PLOT,"");
                      break;

           case ALLC: StripChart(hDC,0,0,0,0,0,(double)(lenkw_in),4096.,PLOT,"");
                      StripChart(hDC,1,0,0,0,0,(double)(gaspd_in-2048),2048.,PLOT,"");
                      StripChart(hDC,2,0,0,0,0,(double)(brems_in-2048),2048.,PLOT,"");
                      StripChart(hDC,5,0,0,0,0,(double)(digital_in & 0x01 ? 0 : 1),2.,PLOT,"");
                      StripChart(hDC,6,0,0,0,0,(double)(digital_in>>1 & 0x01),2.,PLOT,"");
                      StripChart(hDC,7,0,0,0,0,(double)(digital_in>>2 & 0x01),2.,PLOT,"");
                      StripChart(hDC,8,0,0,0,0,(double)(digital_in>>3 & 0x01),2.,PLOT,"");
                      break;

             default: break;
         }

         EndPaint(hWnd,&ps);
         break; // End of WM_PAINT

    case WM_CLOSE:
         KillTimer(hWnd,T_ID_STRIPCHARTTIMING);
         DestroyWindow(hWnd);
         break;

         default: return DefWindowProc(hWnd,Message,wParam,lParam);
 }
 return 0L;
} // End of WndProc


void AddExt(LPSTR Name,LPSTR Ext)
{
  LPSTR pTptr;

  pTptr=Name;
  while (*pTptr && *pTptr != '.')
    pTptr++;
  if (*pTptr != '.')
    lstrcat(Name,Ext);
}


// This is used for doing all system settings

BOOL FAR PASCAL ConfigureMsgProc(HWND hWndDlg,WORD Message,WORD wParam,LONG lParam)
{
  static int timesCalled = 0;  // Used to assure that default model parameters are not changed
  static char szBeginCode[12];
  char szWorkOnBeginCode[12];   // strtok must work on a special copy, it destroyes its argument
  char szSt[] = {'0','0','\0'};  // if tokenization does not fill all first_glimpse.timecode
  char szMi[] = {'0','0','\0'};  // elements, we assume a value of 00
  char szSe[] = {'0','0','\0'};
  char szFr[] = {'0','0','\0'};
  BYTE bcdStd;
  BYTE bcdMin;
  BYTE bcdSec;
  BYTE bcdFrm;
  int length;

  static char szEndCode[12];
  char szWorkOnEndCode[12];      // strtok must work on a special copy, it destroyes its argument

  static char name[9] = "testrun";

  static char par_noisefrequ[11];
  static char par_noiseamplitude[11];

  static char def_noisefrequ[11];
  static char def_noiseamplitude[11];

  // Use an interim bitfield to hold check box settings; this is because the user can cancel the settings
  // he made ("Abbruch" - button). So, you must not he made ("Abbruch" - button). So, you must not
  static struct interim_tag {
    BYTE ison_run_tacho:1;                                // Drive tachometer (D/A) or not
    BYTE ison_run_cursor:1;                               // Draw cross for video projection
    BYTE ison_run_mirror:1;                               // Use steering simulation with mirror
    BYTE ison_do_sendserial:1;                            // Send data via COM3 to another computer
    BYTE fillbits:4;
  } interim;

  switch(Message)
  {
    case WM_INITDIALOG:
         cwCenter(hWndDlg,0);

         timesCalled++;

         interim.ison_run_tacho        = glo_adj.run_tacho;
         interim.ison_run_cursor       = glo_adj.run_cursor;
         interim.ison_run_mirror       = glo_adj.run_mirror;
         interim.ison_do_sendserial    = glo_adj.do_sendserial;

         if (glo_adj.run_tacho)
           CheckDlgButton(hWndDlg,RUNTACHO,1);

         if (glo_adj.run_cursor)
           CheckDlgButton(hWndDlg,RUNCURSOR,1);

         if (glo_adj.run_mirror)
           CheckDlgButton(hWndDlg,RUNMIRROR,1);

         if (glo_adj.do_sendserial)
           CheckDlgButton(hWndDlg,SENDSERIAL,1);

         DropBeginCode2Buf(szBeginCode);
         SetDlgItemText(hWndDlg,RUN_BEGIN_EDI,szBeginCode);
         DropEndCode2Buf(szEndCode);
         SetDlgItemText(hWndDlg,RUN_END_EDI,szEndCode);
         SetDlgItemText(hWndDlg,NAMES_EDI,name);
         break;

    case WM_CLOSE:
         PostMessage(hWndDlg,WM_COMMAND,IDCANCEL,0L);
         break;

    case WM_COMMAND:
         switch(wParam)
         {
           case RUNTACHO:
                if (IsDlgButtonChecked(hWndDlg,RUNTACHO)) interim.ison_run_tacho=1;
                else interim.ison_run_tacho=0;
                break;

           case RUNCURSOR:
                if (IsDlgButtonChecked(hWndDlg,RUNCURSOR)) interim.ison_run_cursor=1;
                else interim.ison_run_cursor=0;
                break;

           case RUNMIRROR:
                if (IsDlgButtonChecked(hWndDlg,RUNMIRROR)) interim.ison_run_mirror=1;
                else interim.ison_run_mirror=0;
                break;

           case SENDSERIAL:
                if (IsDlgButtonChecked(hWndDlg,SENDSERIAL)) interim.ison_do_sendserial=1;
                else interim.ison_do_sendserial=0;
                break;

           case BUTT_GET_INTERVAL:
           case BUTT_SET_INTERVAL:
                MessageBox(NULL,"Das mach´ ich bei Gelegenheit","Sorry, noch nicht implementiert",MB_OK|MB_ICONEXCLAMATION);
                break;

           case IDOK:
                // >> Pass on checkbox state info
                glo_adj.run_tacho        = interim.ison_run_tacho;
                glo_adj.run_cursor       = interim.ison_run_cursor;
                glo_adj.run_mirror       = interim.ison_run_mirror;
                glo_adj.do_sendserial    = interim.ison_do_sendserial;

                // >> Read start tape position

                GetDlgItemText(hWndDlg,RUN_BEGIN_EDI,szBeginCode,12);
                strcpy(szWorkOnBeginCode,szBeginCode);           // Working copy

                strcpy(szSt,strtok(szWorkOnBeginCode,":,;./"));   // Tokenization
                strcpy(szMi,strtok(NULL,":,;./"));
                strcpy(szSe,strtok(NULL,":,;./"));
                strcpy(szFr,strtok(NULL,"!"));

                if ((length=lstrlen(szSt)) < 2 || (length=lstrlen(szMi)) < 2 ||
                    (length=lstrlen(szSe)) < 2 || (length=lstrlen(szFr)) < 2)
                {
                  MessageBox(hWndMain,"Achtstellig eingeben ! z.B.: \"00:01:00:00\"; Trennzeichen: , . ; /",
                  "Eingabefehler Laufzeit-Beginn",MB_APPLMODAL);
                  break;
                }

                // Convert elements to packed BCD format, filllownibble must be
                // called first, because it overwrites the whole byte

                filllownibble(&bcdStd,szSt[1]);
                filllownibble(&bcdMin,szMi[1]);
                filllownibble(&bcdSec,szSe[1]);
                filllownibble(&bcdFrm,szFr[1]);

                fillhighnibble(&bcdStd,szSt[0]);
                fillhighnibble(&bcdMin,szMi[0]);
                fillhighnibble(&bcdSec,szSe[0]);
                fillhighnibble(&bcdFrm,szFr[0]);

                // Pass it to global system info structure
                glo_adj.starttc_st=bcdStd;
                glo_adj.starttc_mi=bcdMin;
                glo_adj.starttc_se=bcdSec;
                glo_adj.starttc_fr=bcdFrm;

                if (bcdStd == 0x00 && bcdMin == 0x00 && bcdSec == 0x00 && bcdFrm == 0x00)
                {
                  MessageBox(hWndDlg,"Kein Timecode eingegeben","Eingabefehler Laufzeit-Beginn",MB_ICONEXCLAMATION);
                  break;
                }

                // >> Read end tape position

                GetDlgItemText(hWndDlg,RUN_END_EDI,szEndCode,12);
                strcpy(szWorkOnEndCode,szEndCode);           // Working copy

                strcpy(szSt,strtok(szWorkOnEndCode,":,;./")); // Tokenization
                strcpy(szMi,strtok(NULL,":,;./"));
                strcpy(szSe,strtok(NULL,":,;./"));
                strcpy(szFr,strtok(NULL,"!"));

                if ((length=lstrlen(szSt)) < 2 || (length=lstrlen(szMi)) < 2 ||
                    (length=lstrlen(szSe)) < 2 || (length=lstrlen(szFr)) < 2)
                {
                  MessageBox(hWndDlg,"Achtstellig eingeben ! z.B.: \"00:01:00:00\"; Trennzeichen: , . ; /",
                  "Eingabefehler Laufzeit-Ende",MB_ICONEXCLAMATION);
                  break;
                }

                // Convert elements to packed BCD format, filllownibble must be
                // called first, because it overwrites the whole byte

                filllownibble(&bcdStd,szSt[1]);
                filllownibble(&bcdMin,szMi[1]);
                filllownibble(&bcdSec,szSe[1]);
                filllownibble(&bcdFrm,szFr[1]);

                fillhighnibble(&bcdStd,szSt[0]);
                fillhighnibble(&bcdMin,szMi[0]);
                fillhighnibble(&bcdSec,szSe[0]);
                fillhighnibble(&bcdFrm,szFr[0]);

                // Pass it to global system info structure
                glo_adj.endtc_st=bcdStd;
                glo_adj.endtc_mi=bcdMin;
                glo_adj.endtc_se=bcdSec;
                glo_adj.endtc_fr=bcdFrm;

                if (bcdStd == 0x00 && bcdMin == 0x00 && bcdSec == 0x00 && bcdFrm == 0x00)
                {
                  MessageBox(hWndDlg,"Kein Timecode eingegeben","Eingabefehler Laufzeit-Ende",MB_ICONEXCLAMATION);
                  break;
                }

                if (!SecondTcQuadBigger(glo_adj.starttc_st,glo_adj.starttc_mi,glo_adj.starttc_se,glo_adj.starttc_fr,
                                        glo_adj.endtc_st,glo_adj.endtc_mi,glo_adj.endtc_se,glo_adj.endtc_fr))
                {
                  MessageBox(hWndDlg,"Ende-Timecode ist kleiner als Start-Timecode","Eingabefehler Laufzeit",MB_ICONEXCLAMATION);
                  break;
                }

                // >> Read protocol file name (without extension)
                GetDlgItemText(hWndDlg,NAMES_EDI,name,9);
                strcpy(glo_adj.expname,name);

                glo_adj.isCanceled = FALSE;

                EndDialog(hWndDlg, TRUE);
                break;

           case IDCANCEL:
                glo_adj.isCanceled = TRUE;
                // Ignore data values entered into the controls
                // and dismiss the dialog window returning FALSE
                EndDialog(hWndDlg,FALSE);
                break;
           }
           break; // End of WM_COMMAND

      default: return FALSE;
   }
   return TRUE;
}

// Cue up with a frame on video tape

BOOL FAR PASCAL SU_BANDMsgProc(HWND hWndDlg,WORD Message,WORD wParam,LONG lParam)
{
   static char codestring[] = "00:00:00:00";

   switch(Message)
   {
      case WM_INITDIALOG:
           cwCenter(hWndDlg,0);
           SetDlgItemText(hWndDlg,su_edit,codestring);
           break;

      case WM_CLOSE:
           PostMessage(hWndDlg,WM_COMMAND,IDCANCEL,0L);
           break;

      case WM_COMMAND:
           switch(wParam)
           {
            case IDOK: GetDlgItemText(hWndDlg,su_edit,codestring,12); // String terminator mitrechnen !

                       if (suche_bandstelle(codestring,hWndDlg,su_band_txt2))
                       {
                         if (IDYES == MessageBox(hWndDlg,"Eine weitere Bandstelle Suchen ?","Fertig",MB_ICONQUESTION|MB_YESNO))
                         {
                           SetFocus(hWndDlg);
                           break;
                         }
                       }
                       else
                       {
                         SetFocus(hWndDlg);
                         break;
                       }

                       EndDialog(hWndDlg,TRUE);
                       break;

        case IDCANCEL: EndDialog(hWndDlg,FALSE);
                       break;
           }

           break; // End of WM_COMMAND

      default: return FALSE;
   }
   return TRUE;
} // End of SU_BANDMsgProc

// Direkte Recordersteuerung durch Tastenklicken mit der Maus

BOOL FAR PASCAL RECORDERMsgProc(HWND hWndDlg,WORD Message,WORD wParam,LONG lParam)
{
  static HWND daughterInstrument; // InstrumentenHandle
  static char winTextString[128]; // Instrumentenbeschriftung
  RECT bounding;

  switch(Message)
  {
    case WM_INITDIALOG:
         {
           int DlgItem;
           int winWidth;
           int winHeight;

           // Set font for all small buttons
           for(DlgItem=200;DlgItem <= 241;DlgItem++)
           {
             SendMessage(GetDlgItem(hWndDlg,DlgItem),WM_SETFONT,globalWorkFont,FALSE);
           }

           // Make analog instrument
           GetClientRect(hWndDlg,&bounding);
           winWidth  = bounding.right-bounding.left;
           winHeight = bounding.bottom-bounding.top;
           bounding.left += winWidth*47/100;
           bounding.right -= winWidth*8/100;
           bounding.top += winHeight*68/100;
           bounding.bottom -= winHeight*4/100;

           daughterInstrument = CreateWindow("analogmeter",
                                             NULL,
                                             WS_CHILD|AMS_LINEMASK,
                                             bounding.left,
                                             bounding.top,
                                             bounding.right-bounding.left,
                                             bounding.bottom-bounding.top,
                                             hWndDlg,
                                             1,
                                             hInst,
                                             NULL);

           SendMessage(daughterInstrument,WM_SETFONT,globalWorkFont,FALSE);

           SendMessage(daughterInstrument,AMM_SETRANGE,0,MAKELONG(0,300)); // STOP,PLAY,FF,REW
           SendMessage(daughterInstrument,AMM_SETSIZE,13,0L);
           sprintf(winTextString,"Variable Tracking;0\xb0;50\xb0;100\xb0;150\xb0;200\xb0;250\xb0;300\xb0");
           SetWindowText(daughterInstrument,winTextString);


           cwCenter(hWndDlg,0);

           // Entdeckt ausgeschalteten Player
           if (!DoesPlayerRespondJVC("COM2")) // Konvention: Player auf COM2
           {
             MessageBox(hWndDlg,
                        "Prüfen Sie den Player und Versuchen Sie es nochmal",
                        "Keine Verbindung zum Videoplayer",
                        MB_OK|MB_TASKMODAL);
             goto GIVEUP;
           }

           if (!SetupCommunicationJVC("COM2"))
           {
             MessageBeep(MB_ICONHAND);
             MessageBox(hWndDlg,
                        "Bitte Verbindungsleitungen prüfen",
                        "Kann Verbindung zum Videoabspieler nicht aufbauen",
                        MB_OK|MB_ICONHAND|MB_TASKMODAL);

GIVEUP:      DestroyWindow(daughterInstrument);
             EndDialog(hWndDlg,FALSE);
             return TRUE;
           }
         }
         break;

    case WM_COMMAND:

         switch(wParam)
         {
           case butStandByOff:
                StandByOffJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butStandByOn:
                StandByOnJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butPlay:
                PlayJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butEject:
                EjectJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butFF:
                FastFwdJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butPreroll:
                PrerollJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butRew:
                RewindJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case butStop:
                StopJVC();
                ShowWindow(daughterInstrument,SW_HIDE);
                break;

           case IDOK:
                ShutDownCommunicationJVC();
                DestroyWindow(daughterInstrument);
                EndDialog(hWndDlg,FALSE);
                break;

           case 200: // Step 1: 0.03
                ForwardVar1Byte(0x10);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,3,0L);
                break;
           case 201: // Step 2  0.06
                ForwardVar1Byte(0x18);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,6,0L);
                break;
           case 202: // Step 3  0.09
                ForwardVar1Byte(0x1e);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,9,0L);
                break;
           case 203: // Step 4  0.12
                ForwardVar1Byte(0x22);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,12,0L);
                break;
           case 204: // Step 5  0.15
                ForwardVar1Byte(0x26);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,15,0L);
                break;
           case 205:// Step 6   0.18
                ForwardVar1Byte(0x28);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,18,0L);
                break;
           case 206: // Step 7  0.21
                ForwardVar2Byte(0x2a,0x05);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,21,0L);
                break;
           case 207: // Step 8  0.24
                ForwardVar1Byte(0x2c);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,24,0L);
                break;
           case 208: // Step 9  0.27
                ForwardVar1Byte(0x2e);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,27,0L);
                break;
           case 209: // Step 10 0.3
                ForwardVar1Byte(0x2f);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,30,0L);
                break;
           case 210: // Step 11 0.33
                ForwardVar2Byte(0x30,0x80);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,33,0L);
                break;
           case 211: // Step 12 0.36
                ForwardVar2Byte(0x31,0xa0);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,36,0L);
                break;
           case 212: // Step 13 0.39
                ForwardVar2Byte(0x32,0xc0);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,39,0L);
                break;
           case 213: // Step 14 0.42
                ForwardVar2Byte(0x34,0x05);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,42,0L);
                break;
           case 214: // Step 15 0.46
                ForwardVar2Byte(0x35,0x40);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,46,0L);
                break;
           case 215: // Step 16 0.50
                ForwardVar1Byte(0x36);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,50,0L);
                break;
           case 216: // Step 17 0.60
                ForwardVar1Byte(0x39);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,60,0L);
                break;
           case 217: // Step 18 0.70
                ForwardVar1Byte(0x3b);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,70,0L);
                break;
           case 218: // Step 19 0.80
                ForwardVar1Byte(0x3d);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,80,0L);
                break;
           case 219: // Step 20 0.90
                ForwardVar2Byte(0x3e,0x78);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,90,0L);
                break;
           case 220: // Step 21 1.00
                ForwardVar1Byte(0x40);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,100,0L);
                break;
           case 221: // Step 22 1.10
                ForwardVar2Byte(0x41,0x50);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,110,0L);
                break;
           case 222: // Step 23 1.20
                ForwardVar2Byte(0x42,0x80);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,120,0L);
                break;
           case 223: // Step 24 1.30
                ForwardVar2Byte(0x43,0xa0);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,130,0L);
                break;
           case 224: // Step 25 1.40
                ForwardVar2Byte(0x44,0xad);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,140,0L);
                break;
           case 225: // Step 26 1.50
                ForwardVar2Byte(0x45,0xaa);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,150,0L);
                break;
           case 226: // Step 27 1.60
                ForwardVar2Byte(0x46,0x90);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,160,0L);
                break;
           case 227: // Step 28 1.70
                ForwardVar2Byte(0x47,0x56);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,170,0L);
                break;
           case 228: // Step 29 1.80
                ForwardVar2Byte(0x48,0x28);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,180,0L);
                break;
           case 229: // Step 30 1.90
                ForwardVar2Byte(0x48,0xf0);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,190,0L);
                break;
           case 230: // Step 31 2.00
                ForwardVar1Byte(0x49);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,200,0L);
                break;
           case 231: // Step 32 2.10
                ForwardVar2Byte(0x4a,0x46);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,210,0L);
                break;
           case 232: // Step 33 2.20
                ForwardVar2Byte(0x4a,0xfa);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,220,0L);
                break;
           case 233: // Step 34 2.30
                ForwardVar2Byte(0x4b,0x96);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,230,0L);
                break;
           case 234: // Step 35 2.40
                ForwardVar2Byte(0x4c,0x28);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,240,0L);
                break;
           case 235: // Step 36 2.50
                ForwardVar1Byte(0x4d);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,250,0L);
                break;
           case 236: // Step 37 2.60
                ForwardVar2Byte(0x4d,0x46);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,260,0L);
                break;
           case 237: // Step 38 2.70
                ForwardVar2Byte(0x4d,0xc8);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,270,0L);
                break;
           case 238: // Step 39 2.80
                ForwardVar2Byte(0x4e,0x50);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,280,0L);
                break;
           case 239: // Step 40 2.90
                ForwardVar2Byte(0x4e,0xc8);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,290,0L);
                break;
           case 240: // Step 41 3.00
                ForwardVar1Byte(0x4f);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,300,0L);
                break;
           case 241: // Step  0.00
                ForwardVar1Byte(0x00);
                ShowWindow(daughterInstrument,SW_SHOWNA);
                SendMessage(daughterInstrument,AMM_SETVALUE,0,0L);
                break;
         }
         break;

    default: return FALSE;
  }
  return TRUE;
} // End of RECORDERMsgProc


// The info dialog box

BOOL FAR PASCAL INFOMsgProc(HWND hWndDlg,WORD Message,WORD wParam,LONG lParam)
{
  switch(Message)
  {
    case WM_INITDIALOG:
         {
           char runtimeTxt[64];
           // Der Satz mit dem Copyrightvermerk wird nachbearbeitet, weil man mit QCWin
           // das Copyright-Zeichen (0xA9 im ANSI/ISO-Zeichensatz) nicht eintippen kann.
           sprintf(runtimeTxt,"Copyright \xa9 1997 Dipl.-Psych. Walter L. Piechulla");

           SetWindowText(GetDlgItem(hWndDlg,info_txt2),runtimeTxt);
         }

         cwCenter(hWndDlg, 0);
         break;

    case WM_CLOSE:
         // Closing the Dialog behaves the same as Cancel
         PostMessage(hWndDlg,WM_COMMAND,IDCANCEL,0L);
         break;

    case WM_COMMAND:
         switch(wParam)
         {
           case IDOK:
                EndDialog(hWndDlg,TRUE);
                break;

           case IDCANCEL:
                // Ignore data values entered into the controls
                // and dismiss the dialog window returning FALSE
                EndDialog(hWndDlg, FALSE);
                break;
         }
         break; // End of WM_COMMAND

    default: return FALSE;
  }
  return TRUE;
}


int nCwRegisterClasses(void)
{
  WNDCLASS wndclass;
  memset(&wndclass,0x00,sizeof(WNDCLASS));

  wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_BYTEALIGNWINDOW;
  wndclass.lpfnWndProc=WndProc;
  wndclass.cbClsExtra=0;
  wndclass.cbWndExtra=0;
  wndclass.hInstance=hInst;
  wndclass.hIcon=LoadIcon(hInst,"STEFF");
  wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
  wndclass.hbrBackground = CreateSolidBrush(RGB(0,0,0));
  wndclass.lpszMenuName=szAppName;
  wndclass.lpszClassName=szAppName;

  if(!RegisterClass(&wndclass))
    return -1;

  // This is the child window for stripcharts:
  wndclass.style=CS_HREDRAW|CS_VREDRAW;
  wndclass.lpfnWndProc=StripWndProc;
  wndclass.hIcon=NULL;
  wndclass.lpszClassName=szStripClass;
  wndclass.hbrBackground=(HBRUSH)(COLOR_SCROLLBAR+1);

  if(!RegisterClass(&wndclass))
    return -1;

  // This is the invisible window class of TheExpWndProc
  wndclass.lpfnWndProc=TheExpWndProc;
  wndclass.lpszClassName=invisibleClass;
  wndclass.hCursor=LoadCursor(hInst,"IngridAsArtist");  // Loading Ingrid´s Cursor
  wndclass.hbrBackground = CreateSolidBrush(RGB(0,0,0));

  if(!RegisterClass(&wndclass))
    return -1;

  return(0);
}


// cwCenter Function, centers a window

void cwCenter(HWND hWnd,int top)
{
  POINT pt;
  RECT swp;
  RECT rParent;
  int iwidth;
  int iheight;

  // Get the rectangles for the parent and the child
  GetWindowRect(hWnd,&swp);
  GetClientRect(hWndMain,&rParent);

  // Calculate the height and width for MoveWindow
  iwidth=swp.right-swp.left;
  iheight=swp.bottom-swp.top;

  // Find the center point and convert to screen coordinates
  pt.x=(rParent.right-rParent.left)/2;
  pt.y=(rParent.bottom-rParent.top)/2;
  ClientToScreen(hWndMain,&pt);

  // Calculate the new x,y starting point
  pt.x=pt.x-(iwidth/2);
  pt.y=pt.y-(iheight/2);

  // Top will adjust the window position, up or down
  if(top)
    pt.y=pt.y+top;

  MoveWindow(hWnd,pt.x,pt.y,iwidth,iheight,FALSE);
}

//  CwUnRegisterClasses Function - be a nice application !
//  Deletes any refrences to windows resources created for this
//  application, frees memory, deletes instance, handles and does
//  clean up prior to exiting the window

void CwUnRegisterClasses(void)
{
  WNDCLASS wndclass;
  memset(&wndclass,0x00,sizeof(WNDCLASS));

  GetClassInfo(hInst,szAppName,&wndclass);
  DeleteObject(wndclass.hbrBackground);
  UnregisterClass(szAppName,hInst);

  memset(&wndclass,0x00,sizeof(WNDCLASS));
  UnregisterClass(szStripClass,hInst);

  memset(&wndclass,0x00,sizeof(WNDCLASS));
  GetClassInfo(hInst,invisibleClass,&wndclass);
  DeleteObject(wndclass.hbrBackground);
  UnregisterClass(invisibleClass,hInst);
}


// Search for a position on tape; as precise as video tape allows

BOOL suche_bandstelle(char *codestring,HWND dialogsHandle,int textID)
{
  ISCUEUPCOMPLETERETURN cueUpRet;
  int length;
  char st[] = {'0','0','\0'};  // if tokenization does not fill all first_glimpse.timecode
  char mi[] = {'0','0','\0'};  // elements, we assume a value of 00 (lower precision desired)
  char se[] = {'0','0','\0'};
  char fr[] = {'0','0','\0'};
  char wk_codestring[13];
  char message[128];

  strcpy(wk_codestring,codestring); // Make working copy of codestring, which strtok is allowed to modify
  strcat(wk_codestring,"!");        // Explicit token indicating end of string

  // tokenize timecode string

  strcpy(st,strtok(wk_codestring,":,;./"));
  strcpy(mi,strtok(NULL,":,;./"));
  strcpy(se,strtok(NULL,":,;./"));
  strcpy(fr,strtok(NULL,"!"));

  if ((length=lstrlen(st)) < 2 || (length=lstrlen(mi)) < 2 || (length=lstrlen(se)) < 2
                              || (length=lstrlen(fr)) < 2)
  {
    MessageBox(hWndMain,"Achtstellig eingeben ! z.B.: 00:01:00:00  Trennzeichen : , . ; /",
                        "Kann diese Eingabe nicht interpretieren",MB_APPLMODAL);
    return FALSE;
  }

  wsprintf(message,"%s wird gesucht",(LPSTR)codestring);
  SetDlgItemText(dialogsHandle,textID,(LPSTR)message);

  SetupCommunicationJVC("COM2");
  CueUpWithDataJVC(atoi(st),atoi(mi),atoi(se),atoi(fr));

  while(TRUE)
  {
    cueUpRet = IsCueUpComplete();

    switch (cueUpRet)
    {
      case CUEUPATENDOFTAPE: // Funktioniert (noch) gar nicht
           //MessageBox(NULL,"Band zu Ende, Timecode nicht auf dem Band","Suche",MB_ICONINFORMATION);
           goto ENDOFSEARCH;

      case CUEUPTCFOUND:
           MessageBox(NULL,"Gefunden","Suche",MB_ICONINFORMATION);
           goto ENDOFSEARCH;

      case CUEUPSTILLRUNNING:
           continue;
    }
  } // Await completion

ENDOFSEARCH:

  ShutDownCommunicationJVC();

  return TRUE;
}


// This function writes only to the high
void fillhighnibble(BYTE *bcd_targetbyte,char sourceNumber)
{
  switch (sourceNumber)
  {
    case '0': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x00); // 00000000
              break;
    case '1': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x10); // 00010000
              break;
    case '2': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x20); // 00100000
              break;
    case '3': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x30); // 00110000
              break;
    case '4': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x40); // 01000000
              break;
    case '5': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x50); // 01010000
              break;
    case '6': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x60); // 01100000
              break;
    case '7': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x70); // 01110000
              break;
    case '8': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x80); // 10000000
              break;
    case '9': *bcd_targetbyte=(*bcd_targetbyte|(BYTE)0x90); // 10010000
              break;
  }
}


// Warning: also zeroes the high nibble

void filllownibble(BYTE *bcd_targetbyte,char sourceNumber)
{
  *bcd_targetbyte=0x00;

  switch (sourceNumber)
  {
    case '0': *bcd_targetbyte=0x0;  // 00000000
              break;
    case '1': *bcd_targetbyte=0x1;  // 00000001
              break;
    case '2': *bcd_targetbyte=0x2;  // 00000010
              break;
    case '3': *bcd_targetbyte=0x3;  // 00000011
              break;
    case '4': *bcd_targetbyte=0x4;  // 00000100
              break;
    case '5': *bcd_targetbyte=0x5;  // 00000101
              break;
    case '6': *bcd_targetbyte=0x6;  // 00000110
              break;
    case '7': *bcd_targetbyte=0x7;  // 00000111
              break;
    case '8': *bcd_targetbyte=0x8;  // 00001000
              break;
    case '9': *bcd_targetbyte=0x9;  // 00001001
              break;
  }
}


// This makes a window look like 3d

void win3dFrame(HWND hwnd,HDC hdc)
{
  HPEN hLGreyPen;
  HPEN hDarkGreyPen;
  HPEN hBackUpPen;
  RECT r;

  GetClientRect(hwnd,&r);
  hLGreyPen=CreatePen(PS_SOLID,1,RGB(225,225,225));
  hDarkGreyPen=CreatePen(PS_SOLID,1,RGB(128,128,128));
  hBackUpPen=SelectObject(hdc,hLGreyPen);
  MoveTo(hdc,r.left,r.bottom);
  LineTo(hdc,r.left,r.top);
  LineTo(hdc,r.right,r.top);
  MoveTo(hdc,r.left+1,r.bottom-1);
  LineTo(hdc,r.left+1,r.top+1);
  LineTo(hdc,r.right-2,r.top+1);

  SelectObject(hdc,hDarkGreyPen);
  MoveTo(hdc,r.right-1,r.top-1);
  LineTo(hdc,r.right-1,r.bottom-1);
  LineTo(hdc,r.left,r.bottom-1);
  MoveTo(hdc,r.right-2,r.top+2);
  LineTo(hdc,r.right-2,r.bottom-2);
  LineTo(hdc,r.left+2,r.bottom-2);

  SelectObject(hdc,hBackUpPen);  // Back in black
  DeleteObject(hDarkGreyPen);
  DeleteObject(hLGreyPen);
}


// Advance tape to start position and return actual start tc as side effect

void tape_to_start(void)
{
  ISCUEUPCOMPLETERETURN cueUpRet = CUEUPSTILLRUNNING;
  char st[3], mi[3], se[3], fr[3];
  int nSt, nMi, nSe, nFr;

  wsprintf(st,"%02x",glo_adj.starttc_st);
  wsprintf(mi,"%02x",glo_adj.starttc_mi);
  wsprintf(se,"%02x",glo_adj.starttc_se);
  wsprintf(fr,"%02x",glo_adj.starttc_fr);

  nSt=atoi(st);
  nMi=atoi(mi);
  nSe=atoi(se);
  nFr=atoi(fr);

  CueUpWithDataJVC(nSt,nMi,nSe,nFr);

  while(TRUE)
  {
    cueUpRet = IsCueUpComplete();

    switch (cueUpRet)
    {
      case CUEUPATENDOFTAPE: // Funktioniert (noch) gar nicht
           //MessageBox(NULL,"Band zu Ende, Timecode nicht auf dem Band","Suche",MB_ICONINFORMATION);
           goto ENDOFSEARCH;

      case CUEUPTCFOUND:
           goto ENDOFSEARCH;

      case CUEUPSTILLRUNNING:
           continue;
    }
  } // Await completion


ENDOFSEARCH: ;
}


// Test if a timecode is the endcode. Do this on seconds precision basis, because the caller
// (TheExpWndProc) has a timing >> 1 frame (0.0333 seconds).

int FAR PASCAL ItsTheEnd(TIMEONLY *tc)
{
  // The TC is not an exact one, so this is the best solution:
  if ((tc->bcdSt == glo_adj.endtc_st && tc->bcdMi == glo_adj.endtc_mi && tc->bcdSe >= glo_adj.endtc_se) ||
      (tc->bcdSt == glo_adj.endtc_st && tc->bcdMi == glo_adj.endtc_mi && tc->bcdSe == glo_adj.endtc_se))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}


// Convert BCD TIMEONLY structure to int format timecode structure

INTFORMTC BCDTC2IntFormatTc(TIMEONLY tc)
{
  char std[3];
  char min[3];
  char sec[3];
  char frm[3];
  INTFORMTC result;

  wsprintf(std,"%02x",tc.bcdSt);                      // wsprintf is a little bit faster than
  wsprintf(min,"%02x",tc.bcdMi);
  wsprintf(sec,"%02x",tc.bcdSe);
  wsprintf(frm,"%02x",tc.bcdFr);
  result.intSt=atoi(std);
  result.intMi=atoi(min);
  result.intSe=atoi(sec);
  result.intFr=atoi(frm);

  return result;
}

// Return timecode seconds passed since start of trial

long FAR PASCAL VideoSecondsMade(INTFORMTC startWas,INTFORMTC nowIs)
{
  long nowSecs = nowIs.intSt*3600 + nowIs.intMi*60 + nowIs.intSe;
  long startSecs = startWas.intSt*3600 + startWas.intMi*60 + startWas.intSe;

  return (nowSecs - startSecs);
}

void DisableOtherMenues(void)
{
  HMENU hMenu = GetMenu(hWndMain);

  EnableMenuItem(hMenu,IDM_EINSTELLUNGEN,MF_GRAYED);

  EnableMenuItem(hMenu,1,MF_BYPOSITION|MF_GRAYED);
    EnableMenuItem(hMenu,IDM_RUNEXP,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_EXIT,MF_GRAYED);

  EnableMenuItem(hMenu,IDM_S_BANDSTELLEANFAHREN,MF_GRAYED);

  EnableMenuItem(hMenu,IDM_R_STEUERUNGA,MF_GRAYED);

  EnableMenuItem(hMenu,5,MF_BYPOSITION|MF_GRAYED);
    EnableMenuItem(hMenu,IDM_H_HANDBUCH,MF_GRAYED);
    EnableMenuItem(hMenu,IDM_H_INFO,MF_GRAYED);

  DrawMenuBar(hWndMain);
}

void ReEnableOtherMenues(void)
{
  HMENU hMenu = GetMenu(hWndMain);

  EnableMenuItem(hMenu,IDM_EINSTELLUNGEN,MF_ENABLED);

  EnableMenuItem(hMenu,1,MF_BYPOSITION|MF_ENABLED);
    EnableMenuItem(hMenu,IDM_RUNEXP,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_EXIT,MF_ENABLED);

  EnableMenuItem(hMenu,IDM_S_BANDSTELLEANFAHREN,MF_ENABLED);

  EnableMenuItem(hMenu,IDM_R_STEUERUNGA,MF_ENABLED);

  EnableMenuItem(hMenu,5,MF_BYPOSITION|MF_ENABLED);
    EnableMenuItem(hMenu,IDM_H_HANDBUCH,MF_ENABLED);
    EnableMenuItem(hMenu,IDM_H_INFO,MF_ENABLED);

  DrawMenuBar(hWndMain);
}

BOOL SecondTcQuadBigger(BYTE h1,BYTE m1,BYTE s1,BYTE f1,BYTE h2,BYTE m2,BYTE s2,BYTE f2)
{
  TIMEONLY bcd1;
  TIMEONLY bcd2;
  INTFORMTC asint1;
  INTFORMTC asint2;
  long frames1;
  long frames2;

  bcd1.bcdSt = h1;
  bcd1.bcdMi = m1;
  bcd1.bcdSe = s1;
  bcd1.bcdFr = f1;

  bcd2.bcdSt = h2;
  bcd2.bcdMi = m2;
  bcd2.bcdSe = s2;
  bcd2.bcdFr = f2;

  asint1 = BCDTC2IntFormatTc(bcd1);
  asint2 = BCDTC2IntFormatTc(bcd2);

  frames1 = (90000L*(long)asint1.intSt)+(1500L*(long)asint1.intMi)+(25L*(long)asint1.intSe)+(long)asint1.intFr;
  frames2 = (90000L*(long)asint2.intSt)+(1500L*(long)asint2.intMi)+(25L*(long)asint2.intSe)+(long)asint2.intFr;

  if (frames2 > frames1)
    return TRUE;
  else
    return FALSE;
}

// Das muß so sein, glaub es einfach
BOOL FAR PASCAL IsEscapeCondition(void)
{
  if (GetAsyncKeyState(VK_ESCAPE))
  {
    if (GetAsyncKeyState(VK_ESCAPE))
    {
      return TRUE;
    }
  }

  return FALSE;
}

BOOL FAR PASCAL SetupCommunicationEDV(LPSTR comName)
{
  if (lstrlen(comName) > 4)
  {
    MessageBeep(MB_ICONHAND);
    MessageBox(NULL,
               "Schnittstellenname länger als 4 Zeichen",
               "Interner Fehler",
               MB_OK|MB_SYSTEMMODAL);
    return FALSE;
  }

  lstrcpy(Com.ComName,comName);

  Com.TheComID = OpenComm(Com.ComName,SZRECEIVEQUEUE,SZTRANSMITQUEUE);

  if (Com.TheComID < 0)
  {
    InternalErrorBox(GetErrIDS4OpenComm(Com.TheComID));
    return FALSE;
  }

  if (GetCommState(Com.TheComID,&Com.TheDCB) != 0)
  {
    InternalErrorBox(IDS_ERR_COM_GETCOMMSTATE);
    CloseComm(Com.TheComID);
    return FALSE;
  }

  Com.TheDCB.BaudRate=19200;
  Com.TheDCB.ByteSize=8;
  Com.TheDCB.Parity=NOPARITY;
  Com.TheDCB.StopBits=ONESTOPBIT;
  Com.TheDCB.fBinary=1;
  Com.TheDCB.fRtsDisable=0;
  Com.TheDCB.fParity=0;
  Com.TheDCB.fOutxCtsFlow=1;
  Com.TheDCB.fOutxDsrFlow=1;
  Com.TheDCB.fDtrDisable=0;
  Com.TheDCB.fOutX=0;
  Com.TheDCB.fInX=0;
  Com.TheDCB.fPeChar=0;
  Com.TheDCB.fNull=0;
  Com.TheDCB.fChEvt=0;
  Com.TheDCB.fDtrflow=1; // In meiner windows.h steht abweichend zur Docu nicht fDtrFlow
  Com.TheDCB.fRtsflow=1; // In meiner windows.h steht abweichend zur Docu nicht fRtsFlow
  Com.TheDCB.XonLim=0;   // wörtlich zu nehmen !
  Com.TheDCB.XoffLim=0;  // wörtlich zu nehmen !

  if (SetCommState(&Com.TheDCB) != 0)
  {
    InternalErrorBox(IDS_ERR_COM_SETCOMMSTATE);
    CloseComm(Com.TheComID);
    return FALSE;
  }

  Com.SetUpOK = TRUE;

  return TRUE;
}

void FAR  PASCAL ShutDownCommunicationEDV(void)
{
  CloseComm(Com.TheComID);
}

// Generische Fehlermeldungsausgabe, benutzt für Fehler, die im Programm selbst begründet sind
void FAR PASCAL InternalErrorBox(int ID)
{
  MessageBeep(MB_ICONHAND);
  LoadString(hInst,ID,szString,sizeof(szString));
  MessageBox(NULL,szString,"Sim97v2: interner Fehler",MB_OK|MB_ICONHAND|MB_SYSTEMMODAL);
}

int FAR PASCAL GetErrIDS4OpenComm(int ErrCode)
{
  switch (ErrCode)
  {
    case IE_BADID:
         return IDS_ERR_COM_IE_BADID;

    case IE_BAUDRATE:
         return IDS_ERR_COM_IE_BAUDRATE;

    case IE_BYTESIZE:
         return IDS_ERR_COM_IE_BYTESIZE;

    case IE_DEFAULT:
         return IDS_ERR_COM_IE_DEFAULT;

    case IE_HARDWARE:
         return IDS_ERR_COM_IE_HARDWARE;

    case IE_MEMORY:
         return IDS_ERR_COM_IE_MEMORY;

    case IE_NOPEN:
         return IDS_ERR_COM_IE_NOPEN;

    case IE_OPEN:
         return IDS_ERR_COM_IE_OPEN;

    default: return IDS_ERR_COM_UNKNOWN;
  }
}

void FAR PASCAL ExplainCommError(int errCode)
{
  int errStrID;

  if (errCode & CE_RXOVER)     //  0x0001 Receive Queue overflow
    CommunicationErrorBox(IDS_ERR_CE_RXOVER);

  if (errCode & CE_OVERRUN)    //  0x0002 Receive Overrun Error
    CommunicationErrorBox(IDS_ERR_CE_OVERRUN);

  if (errCode & CE_RXPARITY)   //  0x0004 Receive Parity Error
    CommunicationErrorBox(IDS_ERR_CE_OVERRUN);

  if (errCode & CE_FRAME)      //  0x0008 Receive Framing error
    CommunicationErrorBox(IDS_ERR_CE_FRAME);

  if (errCode & CE_BREAK)      //  0x0010 Break Detected
    CommunicationErrorBox(IDS_ERR_CE_BREAK);

  if (errCode & CE_CTSTO)      //  0x0020 CTS Timeout
    CommunicationErrorBox(IDS_ERR_CE_CTSTO);

  if (errCode & CE_DSRTO)      //  0x0040 DSR Timeout
    CommunicationErrorBox(IDS_ERR_CE_DSRTO);

  if (errCode & CE_RLSDTO)     //  0x0080 RLSD Timeout
    CommunicationErrorBox(IDS_ERR_CE_RLSDTO);

  if (errCode & CE_TXFULL)     //  0x0100 TX Queue is full
    CommunicationErrorBox(IDS_ERR_CE_TXFULL);

  if (errCode & CE_PTO)        //  0x0200 LPTx Timeout (unmöglich bei COM-Schnittstelle)
    CommunicationErrorBox(IDS_ERR_CE_PTO);

  if (errCode & CE_IOE)        //  0x0400 LPTx I/O Error (unmöglich bei COM-Schnittstelle)
    CommunicationErrorBox(IDS_ERR_CE_IOE);

  if (errCode & CE_DNS)        //  0x0800 LPTx Device not selected (unmöglich bei COM-Schnittstelle)
    CommunicationErrorBox(IDS_ERR_CE_DNS);

  if (errCode & CE_OOP)        //  0x1000 LPTx Out-Of-Paper (unmöglich bei COM-Schnittstelle)
    CommunicationErrorBox(IDS_ERR_CE_OOP);

  if (errCode & CE_MODE)       //  0x8000 Requested mode unsupported
    CommunicationErrorBox(IDS_ERR_CE_MODE);
}

// Fehlermeldungsausgabe, benutzt für Fehler, die bei serieller Kommunikation auftreten
void FAR PASCAL CommunicationErrorBox(int ID)
{
  MessageBeep(MB_ICONHAND);
  LoadString(hInst,ID,szString,sizeof(szString));
  MessageBox(NULL,szString,"Monitor: Kommunikationsfehler",MB_OK|MB_ICONHAND|MB_SYSTEMMODAL);
}

// Zerlegt Integer in Bytes zum Versenden
void FAR PASCAL IntParts(int value,BYTE *a,BYTE *b)
{
  int trans = value;
  BYTE fld[2];

  memcpy(fld,&trans,2);

  *a = fld[0];
  *b = fld[1];
}

// Zerlegt Float in Bytes zum Versenden
void FAR PASCAL FloatParts(float value,BYTE *a,BYTE *b,BYTE *c,BYTE *d)
{
  float trans = value;
  BYTE fld[4];

  memcpy(fld,&trans,4);

  *a = fld[0];
  *b = fld[1];
  *c = fld[2];
  *d = fld[3];
}

// Summiert die ersten sumBytes Bytes in einen BYTE-Puffer auf
int FAR PASCAL MakeChecksum(BYTE *bufPointer,int sumBytes)
{
  int run;
  int sum = 0;

  for(run=0;run < sumBytes;run++)
    sum+= *(bufPointer+run);

  return sum;
}

// Es wird win.ini benutzt
void FAR PASCAL GetSim97ProfileData()
{
  static char retString[128];
  char myName[] = "Sim97";      // Sim97 und Sim97v2 benutzen den selben Abschnitt in win.ini
  char szSt[] = {'0','0','\0'};
  char szMi[] = {'0','0','\0'};
  char szSe[] = {'0','0','\0'};
  char szFr[] = {'0','0','\0'};
  BYTE bcdStd;
  BYTE bcdMin;
  BYTE bcdSec;
  BYTE bcdFrm;

  GetProfileString(myName,"STRTM","00:00:00:00",retString,12);

  strncpy(szSt,retString,2);
  strncpy(szMi,retString+3,2);
  strncpy(szSe,retString+6,2);
  strncpy(szFr,retString+9,2);

  // Convert elements to packed BCD format, filllownibble must be
  // called first, because it overwrites the whole byte

  filllownibble(&bcdStd,szSt[1]);
  filllownibble(&bcdMin,szMi[1]);
  filllownibble(&bcdSec,szSe[1]);
  filllownibble(&bcdFrm,szFr[1]);

  fillhighnibble(&bcdStd,szSt[0]);
  fillhighnibble(&bcdMin,szMi[0]);
  fillhighnibble(&bcdSec,szSe[0]);
  fillhighnibble(&bcdFrm,szFr[0]);

  // Pass it to global system info structure
  glo_adj.starttc_st=bcdStd;
  glo_adj.starttc_mi=bcdMin;
  glo_adj.starttc_se=bcdSec;
  glo_adj.starttc_fr=bcdFrm;

  GetProfileString(myName,"NDTM","00:00:00:00",retString,12);

  strncpy(szSt,retString,2);
  strncpy(szMi,retString+3,2);
  strncpy(szSe,retString+6,2);
  strncpy(szFr,retString+9,2);

  // Convert elements to packed BCD format, filllownibble must be
  // called first, because it overwrites the whole byte

  filllownibble(&bcdStd,szSt[1]);
  filllownibble(&bcdMin,szMi[1]);
  filllownibble(&bcdSec,szSe[1]);
  filllownibble(&bcdFrm,szFr[1]);

  fillhighnibble(&bcdStd,szSt[0]);
  fillhighnibble(&bcdMin,szMi[0]);
  fillhighnibble(&bcdSec,szSe[0]);
  fillhighnibble(&bcdFrm,szFr[0]);


  glo_adj.endtc_st=bcdStd;
  glo_adj.endtc_mi=bcdMin;
  glo_adj.endtc_se=bcdSec;
  glo_adj.endtc_fr=bcdFrm;

  GetProfileString(myName,"RNTCH","1",retString,2);
  if (atoi(retString) == 1) glo_adj.run_tacho=1;
  else glo_adj.run_tacho=0;

  GetProfileString(myName,"RNCRSR","0",retString,2);
  if (atoi(retString) == 1) glo_adj.run_cursor=1;
  else glo_adj.run_cursor=0;

  GetProfileString(myName,"RNMRRR","1",retString,2);
  if (atoi(retString) == 1) glo_adj.run_mirror=1;
  else glo_adj.run_mirror=0;

  GetProfileString(myName,"DSNDSRL","1",retString,2);
  if (atoi(retString) == 1) glo_adj.do_sendserial=1;
  else glo_adj.do_sendserial=0;
}

// Es wird win.ini benutzt
void FAR PASCAL WriteSim97ProfileData()
{
  static char setString[128];
  char myName[] = "Sim97";      // Sim97 und Sim97v2 benutzen den selben Abschnitt in win.ini

  sprintf(setString,"%02x:%02x:%02x:%02x",glo_adj.starttc_st,
          glo_adj.starttc_mi,glo_adj.starttc_se,glo_adj.starttc_fr);
  WriteProfileString(myName,"STRTM",setString);


  sprintf(setString,"%02x:%02x:%02x:%02x",glo_adj.endtc_st,
          glo_adj.endtc_mi,glo_adj.endtc_se,glo_adj.endtc_fr);
  WriteProfileString(myName,"NDTM",setString);

  if (glo_adj.run_tacho) sprintf(setString,"1");
  else sprintf(setString,"0");
  WriteProfileString(myName,"RNTCH",setString);

  if (glo_adj.run_cursor) sprintf(setString,"1");
  else sprintf(setString,"0");
  WriteProfileString(myName,"RNCRSR",setString);

  if (glo_adj.run_mirror) sprintf(setString,"1");
  else sprintf(setString,"0");
  WriteProfileString(myName,"RNMRRR",setString);

  if (glo_adj.do_sendserial) sprintf(setString,"1");
  else sprintf(setString,"0");
  WriteProfileString(myName,"DSNDSRL",setString);
}

// Schreibt den BCD-codierten Start-TC als String in den Puffer buf12
void FAR PASCAL DropBeginCode2Buf(char *buf12)
{
  sprintf(buf12,                  // Muß mindestens 12 Bytes groß sein
          "%02x:%02x:%02x:%02x",
          glo_adj.starttc_st,
          glo_adj.starttc_mi,
          glo_adj.starttc_se,
          glo_adj.starttc_fr);
}

// Schreibt den BCD-codierten End-TC als String in den Puffer buf12
void FAR PASCAL DropEndCode2Buf(char *buf12)
{
  sprintf(buf12,                  // Muß mindestens 12 Bytes groß sein
          "%02x:%02x:%02x:%02x",
          glo_adj.endtc_st,
          glo_adj.endtc_mi,
          glo_adj.endtc_se,
          glo_adj.endtc_fr);
}

double FAR PASCAL Renorm(double oldVal,double oldMin,double oldMax,double newMin,double newMax)
{
  // Unerwartete Outliers durch Extrema ersetzen
  if (oldVal > oldMax)
  {
    oldVal = oldMax;
  }

  if (oldVal < oldMin)
  {
    oldVal = oldMin;
  }

  // Wenn keine Renormierung eingestellt ist, kann man sich die Berechnung sparen
  if (newMin == oldMin && newMax == oldMax)
  {
    return(oldVal);
  }
  else
  {
    return(newMin + (((oldVal - oldMin) / Range(oldMax,oldMin)) * Range(newMax,newMin)));
  }
}

// Spannbreite zwischen zwei Zahlen, egal welche Vorzeichen
double FAR PASCAL Range(double value1,double value2)
{
  double bigOne;
  double smallOne;

  if (value1 > value2)
  {
    bigOne = value1;
    smallOne = value2;
  }
  else if (value2 > value1)
  {
    bigOne = value2;
    smallOne = value1;
  }
  else
  {
    return 0.;
  }

  if (bigOne > 0.)
  {
    return(bigOne - smallOne);
  }
  else if (bigOne < 0.)
  {
    return(-smallOne + bigOne);
  }
  else // bigOne == 0
  {
    return(-smallOne);
  }
}

// HaRdwired die 10 Kontrollinstrumente erzeugen, die während des normalen Versuchslaufs arbeiten
// Esw sind bis zu 15 Instrumente vorgesehen, 3 Spalten - 5 Zeilen
void FAR PASCAL CreateControlInstruments(HWND *vec10HWNDs,HWND hwndParent,HANDLE hInstance,LPRECT bounding)
{
  int run;
  int correcter = (bounding->right - bounding->left)*5/100; // Zentrierhilfe
  int xSpace = GetSystemMetrics(SM_CXFRAME);
  int ySpace = GetSystemMetrics(SM_CYFRAME);
  int commonWidth;
  int commonHeight;
  char winTextString[128];
  // Muß zentriert werden, bei Ausnutzen der vollen Bildschirmbreite würde der Quotient Breite/Höhe
  // schlecht werden. Es reicht, das folgende zu tun:
  bounding->left += correcter;
  commonWidth = (bounding->right-bounding->left)/3 - 6*xSpace; // Gibt guten Breite/Höhe Quotienten
  commonHeight = (bounding->bottom-bounding->top)/5 - 2*ySpace;

  // Create the Instruments (ANAMTR.DLL has registered the class "analogmeter" when started up

  // Spalte 1, Zeile 1
  vec10HWNDs[ANA_LENKRAD_FILM] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+xSpace,bounding->top+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_LENKRAD_FILM,hInstance,NULL);

  // Spalte 2, Zeile 1
  vec10HWNDs[ANA_LENKRAD_VP] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+commonWidth+3*xSpace,bounding->top+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_LENKRAD_VP,hInstance,NULL);

  // Spalte 3, Zeile 1
  // Leer

  // Spalte 1, Zeile 2
  vec10HWNDs[ANA_GAS_FILM] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+xSpace,bounding->bottom/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_GAS_FILM,hInstance,NULL);

  // Spalte 2, Zeile 2
  vec10HWNDs[ANA_GAS_VP] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+commonWidth+3*xSpace,bounding->bottom/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_GAS_VP,hInstance,NULL);

  // Spalte 3, Zeile 2
  // Leer

  // Spalte 1, Zeile 3
  vec10HWNDs[ANA_BREMSE_FILM] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+xSpace,(2*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_BREMSE_FILM,hInstance,NULL);

  // Spalte 2, Zeile 3
  vec10HWNDs[ANA_BREMSE_VP] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+commonWidth+3*xSpace,(2*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_BREMSE_VP,hInstance,NULL);

  // Spalte 3, Zeile 3
  // Leer

  // Spalte 1, Zeile 4
  vec10HWNDs[ANA_V_FILM] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+xSpace,(3*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_V_FILM,hInstance,NULL);

  // Spalte 2, Zeile 4
  vec10HWNDs[ANA_V_VP] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+commonWidth+3*xSpace,(3*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_V_VP,hInstance,NULL);

  // Spalte 3, Zeile 4
  vec10HWNDs[ANA_VAR_TRACK]  = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+2*commonWidth+5*xSpace,(3*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_VAR_TRACK,hInstance,NULL);

  // Spalte 1, Zeile 5
  vec10HWNDs[ANA_HEADING_FILM] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+xSpace,(4*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_HEADING_FILM,hInstance,NULL);

  // Spalte 2, Zeile 5
  vec10HWNDs[ANA_HEADING_VP] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+commonWidth+3*xSpace,(4*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_HEADING_VP,hInstance,NULL);

  // Spalte 3, Zeile 5
  vec10HWNDs[ANA_MIRROR_POS] = CreateWindow("analogmeter",NULL,WS_CHILD|AMS_DIGITAL|AMS_LINEMASK,
    bounding->left+2*commonWidth+5*xSpace,(4*bounding->bottom)/5+ySpace,
    commonWidth,commonHeight,hwndParent,ANA_MIRROR_POS,hInstance,NULL);

  // Setup the Instruments

  SendMessage(vec10HWNDs[ANA_LENKRAD_FILM],AMM_SETRANGE,0,MAKELONG(-720,720));
  SendMessage(vec10HWNDs[ANA_LENKRAD_FILM],AMM_SETSIZE,13,0L);
  sprintf(winTextString,"Lenkrad Film;-720\xb0;-480\xb0;-240\xb0;0\xb0;240\xb0;480\xb0;720\xb0");
  SetWindowText(vec10HWNDs[ANA_LENKRAD_FILM],winTextString);
  SendMessage(vec10HWNDs[ANA_LENKRAD_FILM],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_LENKRAD_VP],AMM_SETRANGE,0,MAKELONG(-720,720));
  SendMessage(vec10HWNDs[ANA_LENKRAD_VP],AMM_SETSIZE,13,0L);
  sprintf(winTextString,"Lenkrad Vp;-720\xb0;-480\xb0;-240\xb0;0\xb0;240\xb0;480\xb0;720\xb0");
  SetWindowText(vec10HWNDs[ANA_LENKRAD_VP],winTextString);
  SendMessage(vec10HWNDs[ANA_LENKRAD_VP],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_GAS_FILM],AMM_SETRANGE,0,MAKELONG(0,100));
  SendMessage(vec10HWNDs[ANA_GAS_FILM],AMM_SETSIZE,11,0L);
  SetWindowText(vec10HWNDs[ANA_GAS_FILM],"Gas Film;0%;20%;40%;60%;80%;100%");
  SendMessage(vec10HWNDs[ANA_GAS_FILM],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_GAS_VP],AMM_SETRANGE,0,MAKELONG(0,100));
  SendMessage(vec10HWNDs[ANA_GAS_VP],AMM_SETSIZE,11,0L);
  SetWindowText(vec10HWNDs[ANA_GAS_VP],"Gas Vp;0%;20%;40%;60%;80%;100%");
  SendMessage(vec10HWNDs[ANA_GAS_VP],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_BREMSE_FILM],AMM_SETRANGE,0,MAKELONG(0,100));
  SendMessage(vec10HWNDs[ANA_BREMSE_FILM],AMM_SETSIZE,11,0L);
  SetWindowText(vec10HWNDs[ANA_BREMSE_FILM],"Bremse Film;0%;20%;40%;60%;80%;100%");
  SendMessage(vec10HWNDs[ANA_BREMSE_FILM],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_BREMSE_VP],AMM_SETRANGE,0,MAKELONG(0,100));
  SendMessage(vec10HWNDs[ANA_BREMSE_VP],AMM_SETSIZE,11,0L);
  SetWindowText(vec10HWNDs[ANA_BREMSE_VP],"Bremse Vp;0%;20%;40%;60%;80%;100%");
  SendMessage(vec10HWNDs[ANA_BREMSE_VP],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_V_FILM],AMM_SETRANGE,0,MAKELONG(0,200));
  SendMessage(vec10HWNDs[ANA_V_FILM],AMM_SETSIZE,21,0L);
  SetWindowText(vec10HWNDs[ANA_V_FILM],"Geschwindigkeit Film;0;20;40;60;80;100;120;140;160;180;200");
  SendMessage(vec10HWNDs[ANA_V_FILM],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_V_VP],AMM_SETRANGE,0,MAKELONG(0,200));
  SendMessage(vec10HWNDs[ANA_V_VP],AMM_SETSIZE,21,0L);
  SetWindowText(vec10HWNDs[ANA_V_VP],"Geschwindigkeit Vp;0;20;40;60;80;100;120;140;160;180;200");
  SendMessage(vec10HWNDs[ANA_V_VP],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_VAR_TRACK],AMM_SETRANGE,0,MAKELONG(0,300));
  SendMessage(vec10HWNDs[ANA_VAR_TRACK],AMM_SETSIZE,13,0L);
  SetWindowText(vec10HWNDs[ANA_VAR_TRACK],"Variable Tracking;0%;50%;100%;150%;200%;250%;300%");

  SendMessage(vec10HWNDs[ANA_HEADING_FILM],AMM_SETRANGE,0,MAKELONG(-45,45));
  SendMessage(vec10HWNDs[ANA_HEADING_FILM],AMM_SETSIZE,13,0L);
  sprintf(winTextString,"Unbenutzt;-45\xb0;-30\xb0;-15\xb0;0\xb0;15\xb0;30\xb0;45\xb0");
  SetWindowText(vec10HWNDs[ANA_HEADING_FILM],winTextString);
  SendMessage(vec10HWNDs[ANA_HEADING_FILM],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_HEADING_VP],AMM_SETRANGE,0,MAKELONG(-45,45));
  SendMessage(vec10HWNDs[ANA_HEADING_VP],AMM_SETSIZE,13,0L);
  sprintf(winTextString,"Delta Kurswinkelfehler;-45\xb0;-30\xb0;-15\xb0;0\xb0;15\xb0;30\xb0;45\xb0");
  SetWindowText(vec10HWNDs[ANA_HEADING_VP],winTextString);
  SendMessage(vec10HWNDs[ANA_HEADING_VP],AMM_SETVALUE,0,0L);

  SendMessage(vec10HWNDs[ANA_MIRROR_POS],AMM_SETRANGE,0,MAKELONG(-25,25));
  SendMessage(vec10HWNDs[ANA_MIRROR_POS],AMM_SETSIZE,21,0L);
  sprintf(winTextString,"Kurswinkelfehler;-25\xb0;-20\xb0;-15\xb0;-10\xb0;-5\xb0;0\xb0;5\xb0;10\xb0;15\xb0;20\xb0;25\xb0");
  SetWindowText(vec10HWNDs[ANA_MIRROR_POS],winTextString);
  SendMessage(vec10HWNDs[ANA_MIRROR_POS],AMM_SETVALUE,0,0L);

  // Arbeitsfonts für die Analoganzeigen setzen
  for(run=ANA_LENKRAD_FILM;run <= ANA_MIRROR_POS;run++)
  {
    SendMessage(vec10HWNDs[run],WM_SETFONT,globalWorkFont,FALSE);
  }
}

// Aus Highbyte und Lowbyte einen Integer zusammenbauen. Dient zum erzeugen von speedOnFilm
// und lenkwOnFilm
int FAR PASCAL RebuildInteger(BYTE low,BYTE high)
{
  union janusKopf_tag {
    int result;
    BYTE raw[2];
  } janusKopf;

  janusKopf.raw[0] = low;
  janusKopf.raw[1] = high;

  return janusKopf.result;
}

int FAR PASCAL RebuildIntegerFromByte(BYTE raw)
{
  return((int)(WORD)raw);
}

// Sollenkwinkel ist trotz Glättung zu unruhig, bedämpfung bei Geradeausfahrt.
// Nur für die Spiegelansteuerung, nicht zum weitermelden an Monitor!
int FAR PASCAL LenkBeruhigung(int sollLenkWinkel,double currentSpeed)
{
  int abw;

  // Schwankungen um 0 bei schneller Geradeausfahrt dämpfen
  if ((abw=abs(sollLenkWinkel)) <= 25 && currentSpeed > 110)
  {
    switch(abw)
    {
      case 0:  return 0;
      case 1:  return 0;   // 1/25
      case 2:  return 0;   // 2/25
      case 3:  return 0;   // 3/25
      case 4:  return 0;   // 4/25
      case 5:  return 1;   // 5/25
      case 6:  return 1;   // 6/25
      case 7:  return 1;   // 7/25
      case 8:  return 2;   // 8/25
      case 9:  return 3;   // 9/25
      case 10: return 4;   // 10/25
      case 11: return 4;   // 11/25
      case 12: return 5;   // 12/25
      case 13: return 6;   // 13/25
      case 14: return 7;   // 14/25
      case 15: return 9;   // 15/25
      case 16: return 10;  // 16/25
      case 17: return 11;  // 17/25
      case 18: return 12;  // 18/25
      case 19: return 14;  // 19/25
      case 20: return 16;  // 20/25
      case 21: return 17;  // 21/25
      case 22: return 19;  // 22/25
      case 23: return 21;  // 23/25
      case 24: return 23;  // 24/25
      case 25: return 25;  // 25/25
    }
  }

  return sollLenkWinkel;
}

int GetIntFromJankerBCD(BYTE hi,BYTE lo)
{
  int einer;
  int zehner;
  int hunderter;
  int tausender;

  einer = (int)getLoNibble(lo);
  zehner = 10 * (int)getHiNibble(lo);
  hunderter = 100 * (int)getLoNibble(hi);
  tausender = 1000 * (int)getHiNibble(hi);

  return(tausender + hunderter + zehner + einer);
}

BYTE getHiNibble(BYTE b)
{
  return(b >> 4);
}


BYTE getLoNibble(BYTE b)
{
  BYTE interim = b << 4;
  return(interim >> 4);
}

//int eliminateSegments(int segmentedLW,int straight)
//{
//  //xxxx not used, anachronistic
//  int result;
//
//  if (abs(segmentedLW - lastLW) > 300)
//  {
//    // Muss Segmentwechsel sein
//    if (currentSeg == LEFTSEG)
//    {
//      currentSeg = RIGHTSEG;
//    }
//    else
//    {
//      currentSeg = LEFTSEG;
//    }
//  }
//
//  switch (currentSeg)
//  {
//    case LEFTSEG:
//         result = segmentedLW - straight;   // Nullstellungskorrektur
//         break;
//
//    case RIGHTSEG:
//         result = straight + (360 - segmentedLW);
//         result = -result;                  // Links ist bei mir positiv
//         break;
//  }
//
//  lastLW = segmentedLW;
//  return -result;
//}






