Coder Social home page Coder Social logo

juliaspacemissiondesign / frametransformations.jl Goto Github PK

View Code? Open in Web Editor NEW
18.0 2.0 2.0 3.19 MB

A modern, high-performance and comprehensive set of tools for transformations between any standard and user-defined reference frame.

License: MIT License

Julia 100.00%
julia reference-frames astrodynamics astronomical-algorithms astronomy orbital-mechanics state-transformations

frametransformations.jl's Introduction

FrameTransformations.jl

A modern high-performance set of tools for transformations between standard and user-defined reference frame.

Stable Documentation Dev Documentation Build Status codecov Code Style: Blue

Are you in search of fundamental routines for efficient and extensible frames transformations?
If so, this package is the ideal starting point. FrameTransformations.jl is designed to provide users with the ability to create a customized, efficient, flexible, and extensible axes/point graph models for mission analysis and space mission design purposes.

Features

  • Convert between different time scales and representations (via Tempo.jl);
  • Read binary ephemeris files (via Ephemerides.jl or CalcephEphemeris.jl)
  • Create custom reference frame systems with both standard and user-defined points and axes.
  • Transform states and their higher-order derivatives between different frames (up to jerk)

All of this seamlessly integrated with ForwardDiff.jl.

Installation

This package can be installed using Julia's package manager:

julia> import Pkg

julia> Pkg.add("FrameTransformations.jl");

Documentation

For further information on this package and its tutorials please refer to the stable documentation.

Support

If you found this package useful, please consider starring the repository. We also encourage you to take a look at other astrodynamical packages of the JSMD organisation.

frametransformations.jl's People

Contributors

andreapasquale94 avatar micheleceresoli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

leilah-mc rjsabet

frametransformations.jl's Issues

Ephemeris Data Rework

The way data for points and axes taken from ephemeris files is handled should be reworked. At the current status, the ephemeris provider is embedded into the frame system and the dedicated add_point_ephemeris! and add_axes_ephemeris! functions automatically look into that specific provider.

This system is however not very flexible, especially when considering other type of points\axes whose data may be taken from similar provider structures, e.g., IPF files or points whose data has been integrated.

The proposal is to remove from the frame system the field storing the ephemeris provider and instead pass it as an additional argument to the dedicated add_pont! and add_axes! functions.

Precompilation fails due to parsing of `eop_iau2000a.txt`

Full trace:

julia> using FrameTransformations
Precompiling FrameTransformations
        Info Given FrameTransformations was explicitly requested, output will be shown live 
[ Info: Downloading file 'eop_iau2000a.txt' from 'https://datacenter.iers.org/data/csv/finals2000A.all.csv' with cURL.
ERROR: LoadError: MethodError: Cannot `convert` an object of type SubString{String} to an object of type Float64

Closest candidates are:
  convert(::Type{N}, ::Tempo.Epoch{S, N}) where {S, N}
   @ Tempo ~/.julia/packages/Tempo/dhNvQ/src/epoch.jl:217
  convert(::Type{T}, ::T) where T
   @ Base Base.jl:84
  convert(::Type{T}, ::Number) where T<:Number
   @ Base number.jl:7
  ...

Stacktrace:
  [1] setindex!(A::Vector{Float64}, x::SubString{String}, i1::Int64)
    @ Base ./array.jl:1021
  [2] _unsafe_copyto!(dest::Vector{Float64}, doffs::Int64, src::Vector{Any}, soffs::Int64, n::Int64)
    @ Base ./array.jl:299
  [3] unsafe_copyto!
    @ Base ./array.jl:353 [inlined]
  [4] _copyto_impl!
    @ Base ./array.jl:376 [inlined]
  [5] copyto!
    @ Base ./array.jl:363 [inlined]
  [6] copyto!
    @ Base ./array.jl:385 [inlined]
  [7] copyto_axcheck!
    @ Base ./abstractarray.jl:1174 [inlined]
  [8] Vector{Float64}(x::Vector{Any})
    @ Base ./array.jl:673
  [9] get_iers_eop_IAU2000A(url::String; force_download::Bool)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:70
 [10] get_iers_eop_IAU2000A (repeats 2 times)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:48 [inlined]
 [11] #get_iers_eop#1
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:24 [inlined]
 [12] get_iers_eop()
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:23
 [13] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:135
 [14] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [15] include(x::String)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
 [16] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/Earth.jl:3
 [17] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [18] include(x::String)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
 [19] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:21
 [20] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [21] include(x::String)
    @ FrameTransformations ~/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:1
 [22] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:17
 [23] include
    @ Base ./Base.jl:495 [inlined]
 [24] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
    @ Base ./loading.jl:2216
 [25] top-level scope
    @ stdin:3
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:135
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/Earth.jl:3
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:1
in expression starting at stdin:3
  โœ— FrameTransformations
  0 dependencies successfully precompiled in 19 seconds. 73 already precompiled.

ERROR: The following 1 direct dependency failed to precompile:

FrameTransformations [5be70612-1674-42c4-a038-c376b6dc81ed]

Failed to precompile FrameTransformations [5be70612-1674-42c4-a038-c376b6dc81ed] to "/home/nre/.julia/compiled/v1.10/FrameTransformations/jl_9vVBKz".
[ Info: Downloading file 'eop_iau2000a.txt' from 'https://datacenter.iers.org/data/csv/finals2000A.all.csv' with cURL.
ERROR: LoadError: MethodError: Cannot `convert` an object of type SubString{String} to an object of type Float64

Closest candidates are:
  convert(::Type{N}, ::Tempo.Epoch{S, N}) where {S, N}
   @ Tempo ~/.julia/packages/Tempo/dhNvQ/src/epoch.jl:217
  convert(::Type{T}, ::T) where T
   @ Base Base.jl:84
  convert(::Type{T}, ::Number) where T<:Number
   @ Base number.jl:7
  ...

Stacktrace:
  [1] setindex!(A::Vector{Float64}, x::SubString{String}, i1::Int64)
    @ Base ./array.jl:1021
  [2] _unsafe_copyto!(dest::Vector{Float64}, doffs::Int64, src::Vector{Any}, soffs::Int64, n::Int64)
    @ Base ./array.jl:299
  [3] unsafe_copyto!
    @ Base ./array.jl:353 [inlined]
  [4] _copyto_impl!
    @ Base ./array.jl:376 [inlined]
  [5] copyto!
    @ Base ./array.jl:363 [inlined]
  [6] copyto!
    @ Base ./array.jl:385 [inlined]
  [7] copyto_axcheck!
    @ Base ./abstractarray.jl:1174 [inlined]
  [8] Vector{Float64}(x::Vector{Any})
    @ Base ./array.jl:673
  [9] get_iers_eop_IAU2000A(url::String; force_download::Bool)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:70
 [10] get_iers_eop_IAU2000A (repeats 2 times)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:48 [inlined]
 [11] #get_iers_eop#1
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:24 [inlined]
 [12] get_iers_eop()
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:23
 [13] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:135
 [14] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [15] include(x::String)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
 [16] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/Earth.jl:3
 [17] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [18] include(x::String)
    @ FrameTransformations.Orient ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
 [19] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:21
 [20] include(mod::Module, _path::String)
    @ Base ./Base.jl:495
 [21] include(x::String)
    @ FrameTransformations ~/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:1
 [22] top-level scope
    @ ~/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:17
 [23] include
    @ Base ./Base.jl:495 [inlined]
 [24] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
    @ Base ./loading.jl:2216
 [25] top-level scope
    @ stdin:3
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/eop.jl:135
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Earth/Earth.jl:3
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/Orient/Orient.jl:1
in expression starting at /home/nre/.julia/packages/FrameTransformations/Y6ARR/src/FrameTransformations.jl:1
in expression starting at stdin:
Stacktrace:
  [1] pkgerror(msg::String)
    @ Pkg.Types ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Pkg/src/Types.jl:70
  [2] precompile(ctx::Pkg.Types.Context, pkgs::Vector{Pkg.Types.PackageSpec}; internal_call::Bool, strict::Bool, warn_loaded::Bool, already_instantiated::Bool, timing::Bool, _from_loading::Bool, kwargs::@Kwargs{io::Base.TTY})
    @ Pkg.API ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Pkg/src/API.jl:1656
  [3] precompile(pkgs::Vector{Pkg.Types.PackageSpec}; io::Base.TTY, kwargs::@Kwargs{_from_loading::Bool})
    @ Pkg.API ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Pkg/src/API.jl:159
  [4] precompile
    @ Pkg.API ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Pkg/src/API.jl:147 [inlined]
  [5] #precompile#114
    @ Pkg.API ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Pkg/src/API.jl:146 [inlined]
  [6] #invokelatest#2
    @ Base ./essentials.jl:889 [inlined]
  [7] invokelatest
    @ Base ./essentials.jl:884 [inlined]
  [8] _require(pkg::Base.PkgId, env::String)
    @ Base ./loading.jl:1957
  [9] __require_prelocked(uuidkey::Base.PkgId, env::String)
    @ Base ./loading.jl:1806
 [10] #invoke_in_world#3
    @ Base ./essentials.jl:921 [inlined]
 [11] invoke_in_world
    @ Base ./essentials.jl:918 [inlined]
 [12] _require_prelocked(uuidkey::Base.PkgId, env::String)
    @ Base ./loading.jl:1797
 [13] macro expansion
    @ Base ./loading.jl:1784 [inlined]
 [14] macro expansion
    @ Base ./lock.jl:267 [inlined]
 [15] __require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1747
 [16] #invoke_in_world#3
    @ Base ./essentials.jl:921 [inlined]
 [17] invoke_in_world
    @ Base ./essentials.jl:918 [inlined]
 [18] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1740

Update ITRF EOP interpolations

At the time being, all the methods associated with ITRF/IERS Earth rotation models use the EOP interpolating the values through the UTC timescale. As an example:

function orient_rot3_itrf_to_gcrf(m::Union{<:IAU2000A,<:IAU2006A}, t::Number)

While this is the common way of creating rotational models, it would be not compliant with the computation of accumulated derivatives of this transformation. This issue was somehow already foreseen during the parsing of EOPs, with the addition of the data interpolated using directly the Terrestrial Time (TT) and therefore removing completely the transformations passing through UTC:

Then:

  • Update IERS associated models EOP interpolation used.
  • Update EOPData documentation to include a description of these TT-based interpolations and their potential usage.

Add legacy inertial frames

Add the following legacy inertial frames that are built-in in SPICE N067:

  • B1950
  • FK4
  • GALACTIC
  • ECLIPB1950

See SPICE chgirf.f for details on these axes definitions.

Remove automatic EOP download

Until now EOP where automatically downloaded from IERS data-center.
While this is a nice automation, it is preferable that the user load the desired EOP.

Therefore, a refactoring of Earth/eop.jl in this direction is necessary.

Standardise IERS routines API and conventions

Increase the consistency between the different conventions and function names. The following conventions should be implemented:

For each convention, the following quantities should be computable:

  • Earth Rotation Angle
  • Frame Bias
  • Mean obliquity of the ecliptic
  • Precession angles (for both 3-angles and 4-angles rotations)
  • Precession matrix
  • Nutation in longitude and obliquity
  • Nutation matrix
  • Greenwich Mean Sidereal Time (GMST)
  • Greenwich Apparent Sidereal Time (GAST)
  • ITRF to GCRF rotation matrix

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

PEF implementation and related functions

This is to insert the Pseudo Earth Fixed (PEF) coordinate frame in the common frames.

It can be obtained by rotating the TOD frame about its Z axis (along the true equatorial plane) by Greenwich Apparent Sidereal Time (GAST). The detailed transformation is:

image

where ๐‘† is the rotation due to sidereal motion and ๐Ž is the true Earth rotation vector.

Linked to resolution of #29.

  • Implement transformation in Orient
  • Implement convenience method in Frames

v2.1 Revision

This is a list of points, in no particular order, that I think needs either fixing or minor changes:

Proposed:

  • Directions should be normalised.
    I believe returning non-unit vectors from the direction routines could lead to many mistakes. Although this change might limit the total use cases, I think there are few applications in which one would benefit from having non-unit directions.

  • What's the purpose of the FramePointFunctions{T, O}(fun::Function) and FrameAxesFunctions{O, N}(fun::Function)? In my understanding, the only case in which you would want to use the same functions across all orders is for fixed points, axes or directions. However, they are not implemented in the corresponding functions.

  • Fixed offset axes being registered as inertial is a mistake because they could be fixed with respect to a set rotating axes, making them non-inertial.

  • I would remove add_axes_root replacing it with add_axes_inertial, as it was in the previous release.

  • I don't agree on having add_axes_inertial require a function with respect to time to compute their orientation. I would re-implement the previous distinction between inertial and projected axes, having add_axes_inertial take as input a DCM and add_axes_projected behaving as the current add_axes_inertial.

  • Add an additional constructor for Rotation, such that Rotation{O}(R:Rotation{O}) = R, to avoid useless conversions in the transform functions.

  • I would use T as the default type letter to indicate the Type of the argument, as it is done in the entire ecosystem. In this version, there are a number of letter changes between type definitions that make code readability hard. For example in ad.jl types are defined with the letter T, whilst in nodes.jl they use the letter N.

  • I believe the ForwardDiff APIs implementation for the Rotation object has been left out from this release (it was available in the previous one).

  • The SVectorNT constructor should be changed. At the moment, since it is a type alias of SVector it introduces two additional constructors on top of StaticArray's original implementation. However, since that is one of the most used packages in the Julia ecosystem, it could cause undesired behaviours and errors.

  • I am not a complete fan of the new ways a lot of constructors (e.g., in Rotation) have been re-written. I don't believe that manually building expressions in every scenario actually helps readability of what's going on.

I'm marking as accepted all the proposed changes that we agree would need to be implemented for the next release.

@andreapasquale94 what do you think?

Make directions return unit-vectors

All direction functions should return unit-vectors as agreed in #65:

  • The existing functions should implement normalisation.
  • add_direction_normalize should be removed.
  • The users should be informed that their custom-made direction functions should return unit-vectors.

Rework IERS series parser

As part of the general IERS framework rework of #40, it is desirable to increase the flexibility and capabilities of the build_series function that is currently responsible of generating efficient functions for the evaluation of the nutation series.

The parser should have the following capabilities:

  • Parse series for both nutation components and CIO X, Y and s + XY/s coordinates.
  • Parse text files: this should avoid the need to translate the original IERS tables into Julia files, avoiding potential transcription errors.
  • Simultaneous computation of multiple quantities: this saves-up time because it avoids the repeated computation of the sine and cosine of series with the same fundamental arguments.
  • Support coefficients with different orders of magnitude: the output should always be in radians but the input coefficients should be translatable to arcseconds via multiplicative factors.

Segmentation Fault with bcrtod axes

When you call twice the bcrtod axes registration for a specific point, either on the same frame system or on a different one a segmentation fault error is thrown.

MWE:

using FrameTransformations

@axes ICRF 1 
@axes IAU_MOON 400001 

@point Moon 301

iau = load(TPC("/home/michele/spice/kernels/pck/pck00011.tpc"));

frames = FrameSystem{3, Float64}();
add_axes_inertial!(frames, ICRF)
add_axes_bcrtod!(frames, IAU_MOON, Moon, iau)


frames2 = FrameSystem{3, Float64}();
add_axes_inertial!(frames2, ICRF)
add_axes_bcrtod!(frames2, IAU_MOON, Moon, iau)

Add GCRF axes ID when ICRF ID is required

Some interface functions allow to define frames with respect to others only if the parent axes have the ICRF ID. Since for most applications GCRF and ICRF are equal, we should also include that ID in the list.

TEME implementation and related functions

The True Equator Mean Equinox is a reference frame used by NORAD TLEs and, therefore would be a nice addition to the common frames.

This can be implemented via a simple rotation about the CEP, starting from TOD:

image

To do that, however, we must:

  • Implement equation of equinoxes.
  • Implement GAST (for convenience)
  • Implement GMST (for convenience)
  • Implement transformation in Orient
  • Implement convenience method in Frames

Rename MEME2000 to EME2000

To improve consistency with other existing software, all instances of MEME2000 should be renamed to EME2000.

Remove Utils submodule

Move all routines in the Utils submodule in JSMDUtils and/or in the other sub-modules.

Test differences between UT1 and TT conversions

The test on the IERS routines highlight that there are differences > 1 arcseconds depending on whether the convert interface or interpolate(IERS_EOP_TT_UT1) function is used to pass from TT to UT1. Is it standard?

Improve test coverage

Basis tests are missing on these features:

  • Add tests on BCI2000 axes.
  • Add tests on topocentric axes.
  • Add tests on surface points.
  • Add tests on geodesy routines.

Other minor tests are also needed across the entire package

Examples of ForwardDiff or JSMDUtils.Autodiff

Thanks to the devs for writing a very useful package! One of the most attractive features to me is the compatibility with ForwardDiff, but I can't figure out how to get that to work. I don't see any examples in the docs or tests. It looks like automatic differentiation is a work in progress. A couple useful use cases that I would like to be able to use are:

  • Jacobian of spacecraft state in frame A, with respect to spacecraft state in frame B
  • Gradient of spacecraft state in frame A, with respect to epoch

The issue I have run into is that update_point! requires the number type of the new state to match the number type of the FrameSystem. However, number types with autodiff get complicated with tagging, and I'm not sure how to construct the FrameSystem to get around this.

Update documentation

  • Remove Utils from the roadmap.
  • Add documentation for loading EOP data.
  • Add light-time transformation tutorial
  • Add tutorial for IERS routines
  • Separate use case examples from tutorials
  • Fix HiFi Earth-Moon example with the new EOP interface
  • Fix some documentation typos
  • Add multi-threading tutorial
  • Add Autodiff tutorial

Loss of accuracy of some tests

The last update reveals a loss of accuracy in some transformations.
I had to change the following test tolerance to 1e-4 from 1e-6.

@test v2as(Rs * v, Rb * v) โ‰ค 1e-4 # TODO : reduce tolerance here

@test v2as(R1 * v, R2 * v) โ‰ค 1e-4 # TODO : reduce tolerance here

The reason should be investigated.
Maybe due to the change from Calceph to Ephemerides in the tests?

Refactor axes and point functions symbols and ID usage

Refactor all point and axes base functions and definitions:

  • Move all the core logic into a function that accept the name as a symbol and the ID as an integer.
  • Create a dispatch that uses AbstractFrameAxes and AbstractFramePoint
  • Create a dispatch for certain axes defintion that does not require the ID or the name (e.g., for ICRF or GCRF)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.