(* $Id: RFSimulationData.m,v 1.7.2.1 2003/01/29 12:31:53 hennig Exp $ *)

(* {{{ Mathematica meta tags *)

(* :Title: Analog Insydes RF Simulation Data Import/Export *)

(* :Authors: Dr.-Ing. Eckhard Hennig, eckhard@kaninkolo.de *)

(* :Summary: This module contains functions for importing/exporting
  RF model and simulation data. *)

(* :Context: AnalogInsydes`RF`RFSimulationData`  *)

(* :Package Version: $Revision: 1.7.2.1 $ *)

(* :Mathematica Version: > 3.0 *)

(* :Copyright: Copyright (C) 2001-2003 by Eckhard Hennig

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)

(* :History:
 * $Log: RFSimulationData.m,v $
 * Revision 1.7.2.1  2003/01/29 12:31:53  hennig
 * + GPL.
 *
 *)

(* :Keywords: Touchstone format *)

(* :Sources: *)

(* :Warning: *)

(* :Limitation: *)

(* :Discussion: *)

(* }}} *)

(* {{{ Version check *)

(******************************************************************************)
(* Analog Insydes RF Simulation Data Import/Export Functions.                 *)
(******************************************************************************)

If[ $VersionNumber < 3.0,
  Print["This program requires Mathematica Version 3.0 or higher."];
  Abort[]
];

(* }}} *)

BeginPackage[ "AnalogInsydes`RF`RFSimulationData`",
              "AnalogInsydes`RF`RFCommon`",
              "AnalogInsydes`Master`",
              "AnalogInsydes`Common`",
              "AnalogInsydes`Miscellaneous`" ]

(* {{{ Protection declarations, exported; Unprotect *)

(******************************************************************************)
(* Protection for exported symbols                                            *)
(******************************************************************************)

`Private`$ExportedProtectedSymbols = {
  ReadTouchstone
}

(******************************************************************************)
(* Unprotection for exported symbols                                          *)
(******************************************************************************)

Unprotect @@ `Private`$ExportedProtectedSymbols;

(* }}} *)

(* {{{ Usage declarations *)

(******************************************************************************)
(* Usage declarations                                                         *)
(******************************************************************************)

ReadTouchstone::usage = "ReadTouchstone[file, opts] reads an s-parameter file
in Touchstone format."

(* }}} *)

(* {{{ Error messages *)

(******************************************************************************)
(* Error messages                                                             *)
(******************************************************************************)

ReadTouchstone::fmtl = "Input file has no format line."
ReadTouchstone::ifmtl = "Invalid format line: `1`"
ReadTouchstone::idatl = "Invalid data line: `1`"

(* }}} *)


Begin["`Private`"]

(* {{{ VersionID *)

(******************************************************************************)
(* VersionID                                                                  *)
(******************************************************************************)

$VersionId = "$Id: RFSimulationData.m,v 1.7.2.1 2003/01/29 12:31:53 hennig Exp $"

(* }}} *)

(* {{{ Option checking rules *)

(* }}} *)

(* {{{ Global constants *)

$TouchstoneFrequencyUnits = {
  "HZ"  -> 1.0,
  "KHZ" -> 1000.0,
  "MHZ" -> 1.0*^6,
  "GHZ" -> 1.0*^9
}

(* }}} *)

(* {{{ ReadTouchstone *)

(******************************************************************************)
(* ReadTouchstone imports s-parameter sets from Touchstone data files.        *)
(******************************************************************************)

Options[ReadTouchstone] = {
  InterpolationOrder -> 1,
  Ports              -> 2,
  ShowHeader         -> False
}

ReadTouchstone[infile:(_String | _InputStream), opts___?OptionQ] :=
  DoReadTouchstone[infile, opts]


DoReadTouchstone[infile_, opts___] :=
  Block[
    { instream, line, records, strstream, format, frequnit, freqvals, spdata, 
      iporder, zref, error, conversion, tracelabels, nports, type, lastfreq,
      state, paramblock, noisedata, noisefreq, linecnt, showhdr, header },

    (* Get option settings. *)
    {iporder, nports, showhdr, error} =
      ResolveAndCheckOptions[ {opts}, ReadTouchstone,
      {InterpolationOrder, Ports, ShowHeader} ];
    If[error, Return[$Failed]];

    (* Open input file if not already done. *)
    instream = If[ Head[infile] === String,
      OpenRead[infile, DOSTextFormat->False],
      infile
    ];
    If[instream === $Failed, Return[$Failed]];

    (* Find format line. *)
    header = {"---------- File Header ----------"};
    While[ (line = Read[instream, String]) =!= EndOfFile,
      If[StringMatchQ[line, "#*"], Break[]];
      If[showhdr, AppendTo[header, line]]
    ];
    AppendTo[header, "---------------------------------"];
    If[showhdr, Print[header // ColumnForm]];
    If[ line === EndOfFile,
      Message[ReadTouchstone::fmtl];
      Return[$Failed]
    ];
    
    (* Read format line. *)
    strstream = StringToStream[ToUpperCase[StringDrop[line, 1]]];
    format = ReadList[strstream, Word, WordSeparators->{" ", "\r", "\n", "\t"}];
    Close[strstream];
    (* Check format line. *)
    If[ !MatchQ[ format, 
          { "HZ" | "KHZ" | "MHZ" | "GHZ", "S" | "Y" | "Z" | "H", 
            "MA" | "RI" | "DB", "R", _ } ],
      Message[ReadTouchstone::ifmtl, line];
      Return[$Failed]
    ];
    frequnit = format[[1]] /. $TouchstoneFrequencyUnits;

    (* Get value of reference impedance. *)
    strstream = StringToStream[Last[format]];
    zref = Read[strstream, Number] // N;
    Close[strstream];

    (* Read contents of Touchstone file. *)
    spdata = {};
    noisedata = {};
    lastfreq = -1.0;
    state = If[nports <= 2, 0, 2];
    While[ (line = Read[instream, String]) =!= EndOfFile,
      (* Skip comment lines. *)
      If[StringMatchQ[line, "!*"], Continue[]];
      (* Read numbers from line. *)
      strstream = StringToStream[line];
      records = ReadList[strstream, Number];
      Close[strstream];
      (* Skip empty lines. *)
      If[Length[records] == 0, Continue[]];

      (* Process raw data. *)
      While[ True,
        Switch[ state,
          0, (* S-parameter data for 1 and 2-ports. *)
            (* Noise data? *)
            If[ First[records] < lastfreq,
              If[nports == 2, state = 1],
              If[ Length[records] == 2*nports^2 + 1,
                lastfreq = First[records];
                AppendTo[spdata, records];
                Break[] ]
            ];
            If[state == 0, state = -1],
          
          1, (* Noise data for 2-ports. *)
            If[ Length[records] == 5,
              AppendTo[noisedata, records];
              Break[]
            ];
            state = -1,

          2, (* First line of s-parameter block for n-ports. *)
            paramblock = {};
            If[ Length[records] == 2*nports + 1,
              paramblock = Join[paramblock, records];
              linecnt = nports - 1;
              state = 3;
              Break[]
            ];
            state = -1,

          3, (* I-th line of s-parameter block for n-ports. *)
            If[ Length[records] == 2*nports,
              paramblock = Join[paramblock, records];
              If[ --linecnt == 0,
                AppendTo[spdata, paramblock];
                state = 2 ];
              Break[]
            ];
            state = -1,

         -1, (* Error: invalid data line. *)
            Message[ReadTouchstone::idatl, line];
            Return[$Failed]
        ]
      ]
    ]; (* End While[ (line = ... *)

    (* Process raw s-parameter data. *)
    spdata = Transpose[spdata];
    freqvals = spdata[[1]]*frequnit;
    spdata = Rest[spdata];
    (* Make data conversion function. *)
    conversion = Switch[ format[[3]],
      "MA", #[[1]] * Exp[I #[[2]]*Pi/180.]&,
      "RI", #[[1]] + I*#[[2]]&,
      (* DB should be dB20 for s-parameters!? *)
      "DB", 10^(#[[1]]/20.) * Exp[I #[[2]]*Pi/180.]&
    ];
    spdata = conversion /@ Partition[spdata, 2];
    (* Make trace labels. *)
    tracelabels = Table[
      format[[2]] <> ToString[i] <> ToString[j],
      {i, 1, nports}, {j, 1, nports}
    ];
    If[nports == 2, tracelabels = Transpose[tracelabels]];
    tracelabels = Flatten[tracelabels];
    (* Compose list of interpolating functions. *)
    spdata = Thread[
      tracelabels ->
        ( Interpolation[
            Transpose[{freqvals, #}], InterpolationOrder->iporder
          ]& /@ spdata )
    ];

    (* Process raw noise data. *)
    If[ Length[noisedata] > 0,
      noisedata = Transpose[noisedata];
      (* Frequency sampling points. *)
      noisefreq = noisedata[[1]]*frequnit;
      (* {Fmin (dB10!?), src, neffr} *)
      noisedata = {#2, 10^(#3/10.)*Exp[I #4*Pi/180.], #5}& @@ noisedata;
      noisedata = Thread[
        {NoiseFigure, SourceReflection, NoiseResistance} ->
          ( Interpolation[
              Transpose[{noisefreq, #}], InterpolationOrder->iporder
            ]& /@ noisedata )
      ]
    ];

    {Join[spdata, noisedata, {Type->format[[2]], ReferenceImpedance->zref}]}
  ]

(* }}} *)

End[]

(* {{{ Protection of exported symbols *)

Protect @@ `Private`$ExportedProtectedSymbols;
Remove[`Private`$ExportedProtectedSymbols];

(* }}} *)

EndPackage[]

(*! Local Variables *)
(* local Variables: *)
(* fill-column: 79 *)
(* comment-column: 20 *)
(* End: *)
